简体   繁体   English

泛型C#中的无界类型参数

[英]Unbounded type parameters in Generics C#

I am new to Generics I started learning generics from MSDN Library 我是泛型新手,我开始从MSDN Library学习泛型

I am unable to understand below points about unbounded type parameters. 我无法理解以下有关无界类型参数的要点。

Type parameters that have no constraints, such as T in public class SampleClass<T>{} , are called unbounded type parameters. 不具有约束,如类型参数Tpublic class SampleClass<T>{}被称为无界类型参数。 Unbounded type parameters have the following rules: 无限类型参数具有以下规则:

  • The != and == operators cannot be used because there is no guarantee that the concrete type argument will support these operators. 不能使用!===运算符,因为不能保证具体类型参数将支持这些运算符。
  • You can compare to null . 您可以将其与null进行比较。 If an unbounded parameter is compared to null , the comparison will always return false if the type argument is a value type. 如果将无界参数与null进行比较,如果type参数为值类型,则比较将始终返回false

I did not find any example of above points. 我没有发现以上几点的任何例子。 It will be great if somebody give me example to understand the points. 如果有人给我榜样来理解要点,那将是很好的。

Note: My question is about the use of != and == operators ... why we can't use those operators in unbounded type and why always return false if Unbounded parameter is compared to null 注意:我的问题是关于!===运算符的使用...为什么我们不能在无界类型中使用这些运算符,以及如果将Unbounded参数与null进行比较,为什么总是返回false

Lets assume for a second this was possible: 让我们假设一下这是可能的:

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

Now assume a struct called F, which has no implementation of the == operator (which no struct has by default). 现在假设有一个名为F的struct ,它没有实现==运算符(默认情况下没有任何结构)。 What should happen here? 这里应该怎么办?

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

The value type has no implementation of == , and we can't defer to Object.ReferenceEquals since this is, again, a value type. 值类型没有实现== ,并且我们不能Object.ReferenceEquals因为它还是值类型。 That is why the compiler doesn't let you do this. 这就是为什么编译器不允许您这样做。

Type parameters can have constraints, for example: where T: BaseClass . 类型参数可以具有约束,例如: where T: BaseClass It means that T must be inherited from BaseClass . 这意味着T必须从BaseClass继承。 If there is no such constraints for a type parameter it called unbounded. 如果类型参数没有此类约束,则称为无界。

There's 2 points from the documentation that you cited: 您引用的文档有两点:

The != and == operators cannot be used because there is no guarantee that the concrete type argument will support these operators. 不能使用!=和==运算符,因为不能保证具体类型参数将支持这些运算符。

This means that the compiler can't know that the type T has the operators == and != . 这意味着编译器无法知道类型T具有运算符==!= A SampleClass<int> would work, but SampleClass<MyType> might not if MyType didn't implement the operators. SampleClass<int>可以工作,但是如果MyType没有实现运算符,则SampleClass<MyType>可能不起作用。 Since T is unbounded, it means that there is no way for the compiler to know what is expected, so it has to take the most restrictive case. 由于T是无界的,这意味着编译器无法知道期望的结果,因此必须采取限制性最强的情况。

You can compare to null. 您可以比较为null。 If an unbounded parameter is compared to null, the comparison will always return false if the type argument is a value type. 如果将无界参数与null进行比较,则如果type参数为值类型,则比较将始终返回false。

This just points out that you can compare to null, but if T is a non-nullable type, then it always returns false. 这只是指出您可以将其与null进行比较,但是如果T是不可为null的类型,则它总是返回false。 Consider the following: 考虑以下:

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

This will generate a compiler warning, since there's no value you can give to i to make the expression true . 这将产生一个编译器警告,因为您没有赋予i值来使表达式为true

My question is about the use of != and == operators ... why we can't use those operators in unbounded type 我的问题是关于!=和==运算符的使用...为什么我们不能在无界类型中使用这些运算符

What would they mean? 他们是什么意思? Normally != and == means "not equal to" and "equal to" but the precise meaning of that depends on whether they are value types or reference types, (and whether they have overloaded those operators, but that also doesn't apply to many bounded types). 通常!===表示“不等于”和“等于”,但是其确切含义取决于它们是值类型还是引用类型(以及它们是否已重载了这些运算符,但这也不适用)许多有界类型)。 Without constraint to at least be one of those != and == have no meaning. 在没有约束的情况下,至少!===具有任何意义。

and why always return false if Unbounded parameter is compared to null. 以及为什么将Unbounded参数与null相比总是返回false。

You are mis-reading. 您看错了。 What is actually said, and quoted by you earlier, is: 实际上,您之前引用的是:

the comparison will always return false if the type argument is a value type . 如果type参数为值类型,则比较将始终返回false。 [Emphasis mine] [强调我的]

This is actually incorrect, a nullable value type can return true in this case: 这实际上是不正确的,在这种情况下,可为空的值类型可以返回true

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

With the above code, we get true if we call new Test<int?>().IsNull(null) and false if we call new Test<int?>().IsNull(1) . 与上面的代码,我们得到true ,如果我们所说的new Test<int?>().IsNull(null)和假,如果我们所说的new Test<int?>().IsNull(1)

With any value type other than Nullable types, we get false because that's the only possible value; 对于除Nullable类型之外的任何其他值类型,我们将得到false因为这是唯一可能的值。 value types other than Nullable<T> cannot be null. Nullable<T>以外的其他值类型不能为null。

It's worth noting that the jitter will pre-empt this, in that when it produces machine code for the method it will know that val == null is always false and replace the code with the constant false . 值得注意的是,抖动将优先于此,因为当它为该方法生成机器代码时,它将知道val == null始终为false并将代码替换为常量false If there is a branch then it need not be jitted. 如果存在分支,则无需将其分支。 Consider: 考虑:

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

When this is jitted for a T that is a non-nullable value type, then it's the same as if the code had been: 如果为非空值类型的T引用了此方法,则它与代码曾经是相同的:

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

Because the jitter knows the first branch can never be hit. 因为抖动知道永远不会碰到第一个分支。 Similarly consider Enumerable.Max() . 类似地,考虑Enumerable.Max() This method returns null on empty sequences that are of a nullable type, and throws an exception otherwise. 此方法对可为空类型的空序列返回null ,否则引发异常。 For the specific overrides this is simple: Enumerable.Max(this IEnumerable<decimal?> source) for example has the code to return null in this case and Enumerable.Max(this IEnumerable<decimal> source) the code to throw. 对于特定的覆盖,这很简单:例如,在这种情况下, Enumerable.Max(this IEnumerable<decimal> source) Enumerable.Max(this IEnumerable<decimal?> source)具有返回null的代码,而Enumerable.Max(this IEnumerable<decimal> source)具有抛出的代码。 For the generic case though, it needs to cover both cases. 但是对于一般情况,它需要涵盖两种情况。 It does so thus: 因此,它这样做:

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;
}

When jitted for a nullable type (reference type of Nullable<T> the jitter knowing in advance that default(TSource) == null is always true means it's the same as if it was jitting: 当为可空类型(引用类型为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;
}

While if the type is aa non-nullable value type then it's the same as if it was jitting: 如果类型是不可为空的值类型,则它与被添加的值相同:

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;
}

As such the fact that == between a non-nullable value type and null is always false (and != always true ) isn't just a restriction, it can actually be useful in allowing us to cover nullable and non-nullable types differently, and the jitter will behave sensibly in removing the branch that isn't used in a particular case. 因此,非空值类型和null之间的==始终为false (而!=始终为true )不仅是一种限制,它实际上还有助于允许我们以不同的方式覆盖nullable和nonnullable类型。 ,并且在删除特定情况下不使用的分支时,抖动将表现得很明智。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM