c# – 构造/创建泛型类型并将类型约束转换为struct-as-base-type约束

通常,我们不能将类型参数T约束为从密封类型(例如结构类型)派生.这将毫无意义,因为只有一种类型可以适合,因此不需要泛型.所以约束如下:

where T : string

要么:

where T : DateTime

这是非法的,这是有充分理由的.

但是,当约束到另一个类型参数时,有时会在其他类型参数被“替换”为实际类型(恰好被密封)时发生.考虑班级:

abstract class ExampleBase<TFromType>
{
  internal abstract void M<TFromMethod>(TFromMethod value) where TFromMethod : TFromType;
}

这是无辜的.在具体化中:

class ExampleOne : ExampleBase<string>
{
  internal override void M<TFromMethod>(TFromMethod strangeString)
  {
    var a = string.IsNullOrEmpty(strangeString);
    Console.WriteLine(a);
    var b = strangeString.Substring(10, 2);
    Console.WriteLine(b);
  }
}

我们使TFromType等于string.这可能是有意义的.除M<>以外的其他成员.但是M<>本身仍然可以使用:代码:

  var e1 = new ExampleOne();
  e1.M("abcdefghijklmnopqrstuvwxyz");

将运行并写:

False
kl

到控制台.所以约束基本上变成了TFromMethod:string的地方,但事情仍然很好.

这个问题是关于TFromType是值类型会发生什么.所以这次我们这样做:

class ExampleTwo : ExampleBase<DateTime>
{
  internal override void M<TFromMethod>(TFromMethod strangeDate)
  {
    // var c = DateTime.SpecifyKind(strangeDate, DateTimeKind.Utc);  // will not compile
    // var d = strangeDate.AddDays(66.5);  // will not compile

    var e = string.Format(CultureInfo.InvariantCulture, "{0:D}", strangeDate);  // OK, through boxing
    Console.WriteLine(e);
    var f = object.ReferenceEquals(strangeDate, strangeDate);
    Console.WriteLine("Was 'strangeDate' a box? " + f);
  }
}

那么为什么不允许来自c和d声明的调用呢?毕竟strangeDate有编译时类型TFromMethod,它被约束为DateTime.所以,奇怪的日期是隐含的DateTime吗?毕竟,这适用于字符串(上面的类ExampleOne).

我希望得到一个答案,它提到了官方C#语言规范中的相关位置.

请注意,在尝试添加d时,键入strangeDate.Ad …使IntelliSense(Visual Studio的自动完成程序)提供了DateTime的所有可访问实例成员的列表,因此很明显IntelliSense认为d中的调用应该是合法的!

当然,在c和d被注释掉后,我们可以使用ExampleTwo(带e和f),代码如下:

  var e2 = new ExampleTwo();
  e2.M(new DateTime(2015, 2, 13));

运行并写出:

Friday, 13 February 2015
Was 'strangeDate' a box? False

最佳答案 引用C#5.0规范:

6.1.10 Implicit conversions involving type parameters

The following implicit conversions exist for a given type parameter T:

  • From T to its effective base class C, from T to any base class of C, and from T to any interface implemented by C. […]

  • […]

10.1.5 Type parameter constraints

The effective base class of a type parameter T is defined as follows:

  • […]
  • If T has no class-type constraint but has one or more type-parameter constraints, its effective base class is the most encompassed type (§6.4.2) in the set of effective base classes of its type-parameter constraints. The consistency rules ensure that such a most encompassed type exists.
  • […]

For the purpose of these rules, if T has a constraint V that is a value-type, use instead the most specific base type of V that is a class-type. This can never happen in an explicitly given constraint, but may occur when the constraints of a generic method are implicitly inherited by an overriding method declaration or an explicit implementation of an interface method.

These rules ensure that the effective base class is always a class-type.

换句话说,给定U = T约束,其中T = string,U的有效基类是字符串.给定U = T约束,其中T = DateTime,U的有效基类不是DateTime,而是ValueType.并且类型参数的唯一相关隐式转换是从类型参数类型到其有效基类.

这似乎会导致一些相当奇怪的行为,正如你所发现的那样,但它必须是一个有意识的决定,因为它已被明确地说明你的行为方式.

我猜想,这项工作会给编译器带来困难,有些情况下编译器会假定它在这种情况下处理引用类型,并且使其工作的好处很小.但就是这样:一个猜测.

点赞