在C# 中指定泛型类型参数时如何使用dynamic
?
我正在通过 C# 书阅读 CLR。我遇到了以下段落:
在为泛型类指定泛型类型参数时,也可以使用动态 (引用类型(、结构(值类型(、接口、委托或方法。执行此操作时, 编译器将动态转换为对象,并将动态属性应用于 有意义的元数据。请注意,您正在使用的泛型代码已经 编译并将类型视为对象;不会执行动态调度,因为 编译器未在泛型代码中生成任何有效负载代码。
据我了解,这段摘录告诉我,我可以将dynamic
用作(例如(类定义中的类型参数。但是在尝试了这个之后,我得出的结论是,它与在类型参数中使用任何其他占位符没有什么不同。所以,我怀疑我的理解是否正确。
using System;
namespace myprogram
{
class A<dynamic> {
public dynamic a;
}
class B {
public Int32 b;
}
class C<T> {
public T c;
}
class Program {
static void Main(string[] args) {
//Cannot implicitly convert type 'string' to 'myprogram.B' [Console.NET]csharp(CS0029)
//A<B> a = new A<B> {a = "foo"};
//Cannot implicitly convert type 'string' to 'myprogram.B' [Console.NET]csharp(CS0029)
//C<B> c = new C<B> {c = "foo"};
//as you can see it does not matter if I use the T or dynamic, the effect is the same
//so, what is the use of using the dynamic in the class definition?
}
}
}
了解类型"参数"和类型"参数"之间的区别非常重要。
考虑一下:
class Foo<T> { } // "T" is a type parameter
...
Foo<int> f; // "int" is a type argument
类型参数声明可以传递给此泛型类型/方法的类型。类型参数本质上是标识符,而不是现有类型。将类型传递给泛型类型/方法时,传递的类型称为类型参数。这与方法参数和参数之间的区别非常相似。
因此,摘录试图说,给定泛型类型,您可以将类型dynamic
传递给它,CLR 将它视为object
。这并不意味着您可以这样做:
class A<dynamic> {
}
这意味着您可以这样做:
var list = new List<dynamic>();
或者使用代码中声明的类型:
C<dynamic> c = new C<dynamic> {c = "foo"};
简短回答:在您的代码中,dynamic
只是一个类型参数名称,您实际上并没有传递参数。
据我了解,这段摘录告诉我,我可以将动态用作(例如(类定义中的类型参数。
类定义中没有类型参数。在泛型类型的定义中,有类型参数。构造泛型类型时,这些是类型参数。所以这个:
class A<dynamic>
{
}
var a = new A<string>();
是具有一个类型参数的泛型类型,其名称为dynamic
。然后是类型的实例化,其中string
作为类型参数传递给类型参数dynamic
。这:
class A<T>
{
}
var a = new A<dynamic>();
是具有一个类型参数的泛型类型,其名称为T
。然后是类型的实例化,其中dynamic
作为类型参数传递给类型参数T
。
你在寻找的是后者,而不是前者。
您可以将 dynamic 用作参数,因此您可以使用
var c = new C<dynamic>();
但是你不能使用具体的类型来构建一个泛型类型,比如
class A<dynamic> { }
class B<int> { }
在下划线中:这是你不应该做的!您在此处定义参数名称!
实际上它不会导致编译错误,但是单词"int"被视为参数名称,就像会有T一样。使用以 T 开头的名称作为泛型类型参数是一个很好的范例,而不是将其与程序其余部分的任何类型混淆。
正如你自己总结的那样,你对 A 和 C 的定义是完全相同的, 除了你很困惑,因为动态这个词与类型无关 这个地方动态。
如果要分配字符串,当然必须创建new C<string>()
或new C<object>()
或任何其他接受字符串的类型。
就我而言,我实际上是将 ExpandoObject 屏蔽为动态,所以我最终使用了这样的代码:
static async Task<TReturn> GenericMethodAsync<TReturn()
{
if (typeof(TReturn) == typeof(ExpandoObject))
// Return an ExpandoObject
}
然后被这样的方法使用
static async Task<dynamic> OtherMethodAsync()
{
return await GenericMethodAsync<ExpandoObject>();
}