通常我们不能将类型参数T
约束为从密封类型(例如struct
类型)派生。这将毫无意义,因为只有一种类型可以适合,因此不需要泛型。所以约束如下:
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
到控制台。所以约束基本上变成where 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
。所以strangeDate
肯定是隐含的DateTime
?毕竟,这适用于string
(class ExampleOne
以上)。
我更希望得到一个答案,该答案涉及官方C#语言规范中的相关位置。
请注意,在尝试添加d
时,键入strangeDate.Ad
...会使IntelliSense(Visual Studio的自动填充程序)提供{{1}的所有可访问实例成员的列表很明显,IntelliSense认为DateTime
中的呼叫应该是合法的!
当然,在d
和c
被注释掉之后,我们可以使用d
(ExampleTwo
和e
)和代码:
f
运行并写出:
Friday, 13 February 2015 Was 'strangeDate' a box? False
答案 0 :(得分:2)
引用C#5.0规范:
6.1.10涉及类型参数的隐式转换
对于给定的类型参数
T
,存在以下隐式转换:
从
T
到其有效基类C
,从T
到C
的任何基类,从T
到任何界面由C
实施。 [...][...]
10.1.5类型参数约束
类型参数
T
的有效基类定义如下:
- [...]
- 如果
T
没有类型约束但有一个或多个类型参数约束,则其有效基类是包含程度最高的类型(§) 6.4.2)在其类型 - 参数约束的有效基类集中。一致性规则确保存在这种最包含的类型。- [...]
出于这些规则的目的,如果
T
的约束V
是值类型,请使用最具体的基本类型{{1}这是一个类类型。这在一个显式给定的约束中永远不会发生,但是当泛型方法的约束由覆盖方法声明或接口方法的显式实现隐式继承时,可能会发生这种情况。这些规则确保有效基类始终是类类型。
换句话说,如果V
约束where U : T
,T = string
的有效基类为U
。如果string
约束为where U : T
,则T = DateTime
的有效基类不是U
,而是DateTime
。并且类型参数的唯一相关隐式转换是从类型参数类型到其有效基类。
这似乎导致了一些相当奇怪的行为,正如你所发现的那样,但它必须是一个有意识的决定,因为它已经明确地说明了你的行为方式。
我猜想,这项工作会给编译器带来困难,有些情况下编译器会假定它在这种情况下处理引用类型,并且使其工作的好处很小。但就是这样:猜测。
答案 1 :(得分:0)
在C#5章13.4.3 Implementation of generic methods
中,它说:
interface I<C>
{
void H<T>(T t) where T: C;
}
class C: I<string>
{
void H<T>(T t)
{
string s = t; // Ok
}
}
他们说:
请注意,从t到s的赋值是有效的,因为T继承了T:string的约束,即使此约束在源代码中不可表达。
我理解为:&#34;即使你不能使用它,你也可以写下来,宣布它,#34;
这里String是一种引用类型,当我们写s = t
时,我们分配引用,它有效分配,因为where T: C
约束允许它。
在DateTime的情况下,当我们写s = t
时,我们复制值,而不是引用。