程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> C#泛型秘訣(7)

C#泛型秘訣(7)

編輯:關於C#

4.11 在泛型字典類中使用foreach

問題

您希望在實現了System. Collections.Generic.IDictionary接口的類型枚舉元素,如System.Collections.Generic.Dictionary 或 System.Collections.Generic.SortedList。

解決方案

最簡單的方法是在foreach循環中使用KeyValuePair結構體:

// 創建字典對象並填充.
  Dictionary<int, string> myStringDict = new Dictionary<int, string>();
  myStringDict.Add(1, "Foo");
  myStringDict.Add(2, "Bar");
  myStringDict.Add(3, "Baz");
  // 枚舉並顯示所有的鍵/值對.
  foreach (KeyValuePair<int, string> kvp in myStringDict)
  {
    Console.WriteLine("key  " + kvp.Key);
    Console.WriteLine("Value " + kvp.Value);
}

討論

非泛型類System.Collections.Hashtable (對應的泛型版本為System.Collections.Generic.Dictionary class), System.Collections.CollectionBase和System.Collections.SortedList 類支持在foreach使用DictionaryEntry類型:

foreach (DictionaryEntry de in myDict)
  {
    Console.WriteLine("key " + de.Key);
    Console.WriteLine("Value " + de.Value);
}

但是Dictionary對象支持在foreach循環中使用KeyValuePair<T,U>類型。這是因為GetEnumerator方法返回一個Ienumerator,而它依次返回KeyValuePair<T,U>類型,而不是DictionaryEntry類型。

KeyValuePair<T,U>類型非常合適在foreach循環中枚舉泛型Dictionary類。DictionaryEntry類型包含的是鍵和值的object對象,而KeyValuePair<T,U>類型包含的是鍵和值在創建一個Dictionary對象是被定義的原本類型。這提高了性能並減少了代碼量,因為您不再需要把鍵和值轉化為它們原來的類型。

閱讀參考

查看MSDN文檔中的“System.Collections.Generic.Dictionary Class”、“System.Collections.Generic. SortedList Class”和“System.Collections.Generic.KeyValuePair Structure”主題。

4.12類型參數的約束

問題

您希望創建泛型類型時,它的類型參數支持指定接口,如IDisposable。

解決方案

使用約束強制泛型的類型參數實現一個或多個指定接口:

public class DisposableList<T> : IList<T>
    where T : IDisposable
  {
    private List<T> _items = new List<T>();
    // 用於釋放列表中的項目的私有方法
    private void Delete(T item)
    {
      item.Dispose();
    }
}

DisposableList只接收實現了IDisposable接口的對象做為它的類型實參。這樣無論什麼時候,從DisposableList對象中移除一個對象時,那個對象的Dispose方法總是被調用。這使得您可以很容易的處理存儲在DisposableList對象中的所有對象。

下面代碼演示了DisposableList對象的使用:

public static void TestDisposableListCls()
    {
        DisposableList<StreamReader> dl = new DisposableList<StreamReader>();
        // 創建一些測試對象.
        StreamReader tr1 = new StreamReader("c:\\boot.ini");
        StreamReader tr2 = new StreamReader("c:\\autoexec.bat");
        StreamReader tr3 = new StreamReader("c:\\config.sys");
        // 在DisposableList內添加一些測試對象.
        dl.Add(tr1);
        dl.Insert(0, tr2);
        dl.Add(tr3);
        foreach(StreamReader sr in dl)
        {
            Console.WriteLine("sr.ReadLine() == " + sr.ReadLine());
        }
        // 在元素從DisposableList被移除之前將調用它們的Dispose方法
        dl.RemoveAt(0);
        dl.Remove(tr1);
        dl.Clear();
}

討論

where關鍵字用來約束一個類型參數只能接收滿足給定約束的實參。例如,DisposableList約束所有類型實參T必須實現IDisposable接口:

public class DisposableList<T> : IList<T>
    where T : IDisposable

這意味著下面的代碼將成功編譯:

DisposableList<StreamReader> dl = new DisposableList<StreamReader>();

但下面的代碼不行:

DisposableList<string> dl = new DisposableList<string>();

這是因為string類型沒有實現IDisposable接口,而StreamReader類型實現了。

除了一個或多個指定接口需要被實現外,類型實參還允許其他約束。您可以強制類型實參繼承自一個指定類,如Textreader類:

public class DisposableList<T> : IList<T>
    where T : System.IO.TextReader, IDisposable

您也可以決定是否類型實參僅為值類型或引用類型。下面的類聲明被約束為只使用值類型:

public class DisposableList<T> : IList<T>
     where T : struct

這個類型聲明為只能使用引用類型:

public class DisposableList<T> : IList<T>
     where T : class

另外,您也可能會需要一些類型實參實現了公有的默認構造方法:

public class DisposableList<T> : IList<T>
     where T : IDisposable, new()

使用約束允許您編寫只接收部分類型實參的泛型類型。如果本節中的解決方案忽略了IDisposable約束,有可能會引發一個編譯錯誤。這是因為並非所有DisaposableList類的類型實參都實現了IDisposable接口。如果您跳過這個編譯期檢查,DisaposableList對象就可能會包含一個沒有公有無參的Dispose方法的對象。些例中將會引發一個運行期異常。

給泛型指定約束強制類的類型實參進行嚴格的類型檢查,並使得您在編譯期發現問題而不是運行期。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved