在 C# 中指定泛型类型参数时如何使用 `dynamic`?

How to use the `dynamic` when specifying generic type arguments in C#?

如何在 C# 中指定泛型类型参数时使用 dynamic

我正在阅读 CLR via C# 一书。我遇到了以下段落:

It is also possible to use dynamic when specifying generic type arguments to a generic class (reference type), a structure (value type), an interface, a delegate, or a method. When you do this, the compiler converts dynamic to Object and applies DynamicAttribute to the various pieces of metadata where it makes sense. Note that the generic code that you are using has already been compiled and will consider the type to be Object; no dynamic dispatch will be performed because the compiler did not produce any payload code in the generic code.

据我所知,这段摘录告诉我可以在(例如)class 定义中使用 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?
        }
    }
}

你可以使用 dynamic 作为参数,所以你可以使用

var c = new C<dynamic>();

但是你不能使用具体类型来构建通用类型,例如

     class A<dynamic> { }
     class B<int> { }

在下划线中:你不应该这样做!您正在此处定义参数名称!

实际上它不会导致编译错误,但单词 "int" 被视为参数名称,与 T 相同。使用以 T 开头的名称作为泛型类型是一个很好的范例参数,不要将其与程序其余部分的任何类型混淆。

正如您自己得出的结论,您对 A 和 C 的定义完全相同, 除了你感到困惑,因为动态这个词与类型无关 动态在这个地方。

如果你想分配一个字符串,当然你必须创建一个 new C<string>()new C<object>() 或任何其他接受字符串的类型。

了解类型 "arguments" 和类型 "parameters" 之间的区别非常重要。

考虑一下:

class Foo<T> { } // "T" is a type parameter

...

Foo<int> f; // "int" is a type argument

类型参数声明您可以传递给此泛型的类型 type/method。类型参数本质上是标识符,而不是现有类型。当您将类型传递给泛型 type/method 时,您传递的类型称为类型参数。这与 the difference between a method parameter and argument.

非常相似

所以摘录试图说给定泛型类型,您可以将类型 dynamic 传递给它,CLR 会将其视为 object。这并不意味着您可以这样做:

class A<dynamic> {

}

这意味着你可以这样做:

var list = new List<dynamic>();

或者使用代码中声明的类型:

C<dynamic> c = new C<dynamic> {c = "foo"};

简短回答:在您的代码中 dynamic 只是一个类型参数名称,您实际上并没有传递参数。

As far as I understand this excerpt tells that I can use the dynamic as a type argument in (e.g.) a class definition.

class 定义中没有类型 参数 。在泛型的定义中有类型参数。当您构造泛型类型时,这些是类型参数。所以这个:

class A<dynamic>
{
}

var a = new A<string>();

是一个泛型,有一个类型参数,名称是dynamic。然后是类型的实例化,其中 string 作为类型参数传递给类型参数 dynamic。这个:

class A<T>
{
}

var a = new A<dynamic>();

是一个泛型,有一个类型参数,名称是T。然后是类型的实例化,其中 dynamic 作为类型参数传递给类型参数 T.

你要找的是后者,而不是前者。

在我的例子中,我实际上是将 ExpandoObject 屏蔽为动态对象,所以我最终使用了如下代码:

static async Task<TReturn> GenericMethodAsync<TReturn()
{
    if (typeof(TReturn) == typeof(ExpandoObject))
        // Return an ExpandoObject
}

然后像这样的方法使用了它

static async Task<dynamic> OtherMethodAsync()
{
    return await GenericMethodAsync<ExpandoObject>();
}