繁体   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