簡體   English   中英

泛型C#中的無界類型參數

[英]Unbounded type parameters in Generics C#

我是泛型新手,我開始從MSDN Library學習泛型

我無法理解以下有關無界類型參數的要點。

不具有約束,如類型參數Tpublic class SampleClass<T>{}被稱為無界類型參數。 無限類型參數具有以下規則:

  • 不能使用!===運算符,因為不能保證具體類型參數將支持這些運算符。
  • 您可以將其與null進行比較。 如果將無界參數與null進行比較,如果type參數為值類型,則比較將始終返回false

我沒有發現以上幾點的任何例子。 如果有人給我榜樣來理解要點,那將是很好的。

注意:我的問題是關於!===運算符的使用...為什么我們不能在無界類型中使用這些運算符,以及如果將Unbounded參數與null進行比較,為什么總是返回false

讓我們假設一下這是可能的:

public class C
{
     public bool AreEqual<T>(T first, T second)
     {
           return first == second;
     }
}

現在假設有一個名為F的struct ,它沒有實現==運算符(默認情況下沒有任何結構)。 這里應該怎么辦?

F first = new F();
F second = new F();
var c = new C();
Console.WriteLine(c.AreEqual(first, second));

值類型沒有實現== ,並且我們不能Object.ReferenceEquals因為它還是值類型。 這就是為什么編譯器不允許您這樣做。

類型參數可以具有約束,例如: where T: BaseClass 這意味着T必須從BaseClass繼承。 如果類型參數沒有此類約束,則稱為無界。

您引用的文檔有兩點:

不能使用!=和==運算符,因為不能保證具體類型參數將支持這些運算符。

這意味着編譯器無法知道類型T具有運算符==!= SampleClass<int>可以工作,但是如果MyType沒有實現運算符,則SampleClass<MyType>可能不起作用。 由於T是無界的,這意味着編譯器無法知道期望的結果,因此必須采取限制性最強的情況。

您可以比較為null。 如果將無界參數與null進行比較,則如果type參數為值類型,則比較將始終返回false。

這只是指出您可以將其與null進行比較,但是如果T是不可為null的類型,則它總是返回false。 考慮以下:

int i = 0;
if (null == i)
{
}

這將產生一個編譯器警告,因為您沒有賦予i值來使表達式為true

我的問題是關於!=和==運算符的使用...為什么我們不能在無界類型中使用這些運算符

他們是什么意思? 通常!===表示“不等於”和“等於”,但是其確切含義取決於它們是值類型還是引用類型(以及它們是否已重載了這些運算符,但這也不適用)許多有界類型)。 在沒有約束的情況下,至少!===具有任何意義。

以及為什么將Unbounded參數與null相比總是返回false。

您看錯了。 實際上,您之前引用的是:

如果type參數為值類型,則比較將始終返回false。 [強調我的]

這實際上是不正確的,在這種情況下,可為空的值類型可以返回true

public class Test<T>
{
  public bool IsNull(T val)
  {
     return val == null;
  }
}

與上面的代碼,我們得到true ,如果我們所說的new Test<int?>().IsNull(null)和假,如果我們所說的new Test<int?>().IsNull(1)

對於除Nullable類型之外的任何其他值類型,我們將得到false因為這是唯一可能的值。 Nullable<T>以外的其他值類型不能為null。

值得注意的是,抖動將優先於此,因為當它為該方法生成機器代碼時,它將知道val == null始終為false並將代碼替換為常量false 如果存在分支,則無需將其分支。 考慮:

public string CallToString(T val)
{
  if (val == null)
    return null;
  else
    return val.ToString();
}

如果為非空值類型的T引用了此方法,則它與代碼曾經是相同的:

public string CallToString(T val)
{
  return val.ToString();
}

因為抖動知道永遠不會碰到第一個分支。 類似地,考慮Enumerable.Max() 此方法對可為空類型的空序列返回null ,否則引發異常。 對於特定的覆蓋,這很簡單:例如,在這種情況下, Enumerable.Max(this IEnumerable<decimal> source) Enumerable.Max(this IEnumerable<decimal?> source)具有返回null的代碼,而Enumerable.Max(this IEnumerable<decimal> source)具有拋出的代碼。 但是對於一般情況,它需要涵蓋兩種情況。 因此,它這樣做:

public static TSource Max<TSource>(this IEnumerable<TSource> source)
{
  if (source == null) throw Error.ArgumentNull("source");
  Comparer<TSource> comparer = Comparer<TSource>.Default;
  TSource value = default(TSource);
  if (value == null)
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      do
      {
        if (!e.MoveNext()) return value;
        value = e.Current;
      } while (value == null);
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (x != null && comparer.Compare(x, value) > 0) value = x;
      }
    }
  }
  else
  {
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
      if (!e.MoveNext()) throw Error.NoElements();
      value = e.Current;
      while (e.MoveNext())
      {
        TSource x = e.Current;
        if (comparer.Compare(x, value) > 0) value = x;
      }
    }
  }
  return value;
}

當為可空類型(引用類型為Nullable<T>抖動時,抖動會事先知道default(TSource) == null始終為true,這意味着它與抖動相同。

public static TSource Max<TSource>(this IEnumerable<TSource> source)
{
  if (source == null) throw Error.ArgumentNull("source");
  Comparer<TSource> comparer = Comparer<TSource>.Default;
  TSource value = null;
  using (IEnumerator<TSource> e = source.GetEnumerator())
  {
    do
    {
      if (!e.MoveNext()) return value;
      value = e.Current;
    } while (value == null);
    while (e.MoveNext())
    {
      TSource x = e.Current;
      if (x != null && comparer.Compare(x, value) > 0) value = x;
    }
  }
  return value;
}

如果類型是不可為空的值類型,則它與被添加的值相同:

public static TSource Max<TSource>(this IEnumerable<TSource> source)
{
  if (source == null) throw Error.ArgumentNull("source");
  Comparer<TSource> comparer = Comparer<TSource>.Default;
  TSource value = default(TSource);
  using (IEnumerator<TSource> e = source.GetEnumerator())
  {
    if (!e.MoveNext()) throw Error.NoElements();
    value = e.Current;
    while (e.MoveNext())
    {
      TSource x = e.Current;
      if (comparer.Compare(x, value) > 0) value = x;
    }
  }
  return value;
}

因此,非空值類型和null之間的==始終為false (而!=始終為true )不僅是一種限制,它實際上還有助於允許我們以不同的方式覆蓋nullable和nonnullable類型。 ,並且在刪除特定情況下不使用的分支時,抖動將表現得很明智。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM