1

正如在 C 3.5 中发现的那样,由于类型擦除,以下内容是不可能的:-

int foo<T>(T bar)
{
    return bar.Length; // will not compile unless I do something like where T : string
}

foo("baz");

我相信这在 C# 和 java 中不起作用的原因是由于一个称为类型擦除的概念,请参阅http://en.wikipedia.org/wiki/Type_erasure

在阅读了动态关键字后,我写了以下内容: -

int foo<T>(T bar)
{
    dynamic test = bar;
    return test.Length;
}

foo("baz"); // will compile and return 3

因此,据我了解,动态将绕过编译时检查,但如果类型已被删除,除非它更深入并使用某种反射,否则它肯定仍然无法解析符号?

以这种方式使用动态关键字是不好的做法,这是否会使泛型更强大一些?

4

6 回答 6

5

动力学和泛型是两个完全不同的概念。如果您想要编译时安全和速度,请使用强类型(泛型或仅标准 OOP 技术,例如继承或组合)。如果您在编译时不知道类型,您可以使用动态,但它们会更慢,因为它们使用运行时调用并且不太安全,因为如果类型没有实现您尝试调用的方法,您将收到运行时错误。

这两个概念不可互换,根据您的具体要求,您可以使用其中一个。

当然,具有以下泛型约束是完全没用的,因为string它是密封类型,不能用作泛型约束:

int foo<T>(T bar) where T : string
{
    return bar.Length;
}

你宁愿有这个:

int foo(string bar)
{
    return bar.Length;
}
于 2013-04-25T08:03:51.633 回答
2

我相信这在 C# 和 java 中不起作用的原因是由于一个称为类型擦除的概念,请参阅http://en.wikipedia.org/wiki/Type_erasure

不,这不是因为类型擦除。无论如何,在 C# 中没有类型擦除(与 Java 不同):运行时为每个不同的类型参数集构造一个不同的类型,不会丢失信息。

它不起作用的原因是编译器对 不了解T,因此只能假设T继承自object,因此只有成员object可用。但是,您可以通过在 T 上添加约束来向编译器提供更多信息。例如,如果您有一个IBar带有Length属性的接口,则可以添加如下约束:

int foo<T>(T bar) where T : IBar
{
    return bar.Length;
}

但是如果你希望能够传递一个数组或一个字符串,它就行不通了,因为该Length属性没有在由两者实现的任何接口中声明,String并且Array......

于 2013-04-25T08:12:23.033 回答
1

不,C# 没有类型擦除——只有 Java 有。

但是如果你只指定 T,没有任何约束,你就不能使用 obj.Lenght,因为 T 实际上可以是任何东西。

foo(new Bar());

以上将解析为 Bar-Class,因此 Lenght 属性可能不可用。只有当你确保 T 这个方法也确实有时,你才能在 T 上使用方法。(这是通过 where 约束完成的。)

使用动态,您可以放松编译时间检查,我建议您不要使用它们来破解泛型。

于 2013-04-25T08:09:44.947 回答
0

在这种情况下,您不会以任何方式从动态中受益。您只需延迟错误,因为如果动态对象不包含Length属性,则会引发异常。如果以通用方法访问该Length属性,我看不出有任何理由不将其限制为肯定具有该属性的类型。

于 2013-04-25T08:07:07.937 回答
0

“Dynamics 是一个强大的新工具,它使与动态语言以及 COM 的互操作变得更容易,并且可以用来替换许多笨拙的反射代码。它们可以用来告诉编译器对一个对象执行操作,对它的检查是推迟到运行时。

最大的危险在于在不适当的上下文中使用动态对象,例如在静态类型的系统中,或者更糟的是,在正确类型的系统中代替接口/基类。”

引自文章

于 2013-04-25T08:10:49.580 回答
0

以为我会对此进行权衡,因为没有人澄清泛型如何“在引擎盖下”工作。上面提到了 T 是一个对象的概念,并且非常清楚。没有谈到的是,当我们编译 C# 或 VB 或任何其他受支持的语言时, - 在中间语言 (IL) 级别(我们编译到的)更类似于汇编语言或等效于 Java 字节码, - 在这个级别,没有泛型!所以新的问题是如何在 IL 中支持泛型?对于访问泛型的每种类型,都会生成一个非泛型版本的代码,它将泛型(例如无处不在的 T)替换为调用它的实际类型。因此,如果您只有一种类型的泛型,例如 List<>,那么这就是 IL 将包含的内容。但是如果你使用许多泛型的实现,然后创建许多特定的实现,并将对原始代码的调用替换为对特定非通用版本的调用。需要明确的是,用作:new MyList() 的 MyList 将在 IL 中替换为 MyList_string() 之类的东西。

这是我对正在发生的事情的(有限)理解。关键是,这种方法的好处在于,繁重的工作是在编译时完成的,并且在运行时不会降低性能 - 这也是为什么泛型可能如此受.NET 开发人员在任何地方和任何地方使用的原因。

不利的一面?如果一个方法或类型被多次使用,那么输出的程序集(EXE 或 DLL)将变得越来越大,这取决于相同代码的不同实现的数量。鉴于 DLL 输出的平均大小 - 我怀疑您是否会认为泛型是一个问题。

于 2018-10-22T08:55:19.753 回答