【问题标题】:When is the generic type resolved in c#?什么时候在 C# 中解析泛型类型?
【发布时间】:2013-07-18 00:57:48
【问题描述】:

根据thisstackoverflow 的回答,C# 中的泛型类型在运行时解析

但是,根据this 的回答,在C# 中,泛型类型在编译时解析

我在这里错过了什么?

换句话说,T 类型是在编译时还是运行时解析的?

更新:

根据 Oded 的回答,在这种情况下,类型是封闭的具体类型(这意味着它将在编译时解析)

class Program
{
    static void Main()
    {
        var t = new Test<int>();
    }  
}

public class Test<T>
{   
}

MSIL 是否有相当于

class Program
{
    static void Main()
    {
        var t = new Test();
    }
}

public class Test<int>
{        
}

【问题讨论】:

  • 您链接到的那两个帖子实际上是在描述不同的概念。
  • 您提供的链接中发布的答案中有哪些部分您不明白No; that's fundamentally impossible. The whole point of generics is that they create compile-time types. You're trying to create a type which is unknown at compile time. You can do it using reflection, though. (typeof(MyClass&lt;&gt;).MakeGenericType(myType))
  • 那么你需要清楚地定义一个类型被“解析”的含义,因为显然至少有两个人用它来表示至少两个不同的东西。我不知道“已解决”类型的特征是什么。我知道重载分辨率的特点;这就是您所说的“已解决”的类型吗?
  • 您更新后的问题的答案是。直到 runtime 才创建构造的泛型类型。在编译时,编译器将确定int 是对应于T 的合法参数,仅此而已。此外:Test&lt;T&gt; 的主体必须包含可以在给定T任何 可能值的情况下编译的代码,而不仅仅是出现在Main 中的值。这是泛型和模板之间的主要区别之一。

标签: c# generics


【解决方案1】:

问题是问题不是很好提出的。两个人声称相反的事情:类型在运行时“解析”,类型在编译时“解析”。

由于它们相互矛盾,因此它们都必须通过“解决”来表示不同的意思。

我不知道“解析”类型意味着什么。不过,我确实知道 重载分辨率 是什么。当被要求解决不涉及dynamic重载解决问题时,C# 编译器根据编译时间确定在编译时调用哪个重载关于泛型类型的信息。例如,如果您有:

static void Main()
{
    var d = new D();
    var p = new P<D>();
    p.N(d);//Displays In class B
}


class B
{
    public void M()// Note, not virtual
    {
        Console.WriteLine("In class B");
    }
} 

class D : B
{
    public new void M()// new, not overload
    {
        Console.WriteLine("In class D");
    }
} 

class P<T> where T : B
{
    public  void N(T t)
    {
        t.M();
    }
}

N 总是调用B.M 即使P&lt;T&gt; 被实例化为P&lt;D&gt;。为什么?因为决定t.M是什么意思的重载解析问题必须在编译P&lt;T&gt;.N的时候解决,而那个时候编译器最好知道t一定是@ 987654331@,所以它选择B.M

如果这不是您所说的“已解决”,请澄清问题。

【讨论】:

  • 您的示例是否显示重载解决方案或绑定?虽然绑定需要重载解析,但名称为M 的仅有的两种方法具有相同的签名,因此“重载解析”只有一个候选者可以使用。我认为“过载解决”的一个更好的例子可能是static bool Compare&lt;T&gt;(T p1, T p2) where T:class { return p1 == p2;} ... Compare("5", 5.ToString())。由于编译器无法知道T 将是String 类型,它无法知道它应该使用String-vs-String 重载== 而不是执行引用比较?
  • 顺便说一句,== 的引用比较是重载,还是其他某种编译器魔法?采用Object 类型参数的== 重载将允许比较任何两种类型,但C# == 运算符不能这样工作。它的行为似乎与任何可以通过正常的重载解决方案实现的行为完全不同。
  • @supercat:好问题。它实际上确实使用了正常的运算符重载决议,并进行了一些小的修改。首先,对x==nullnull!=x等形式的表达式进行特殊处理。然后重载决议正常发生。然后是解决后验证步骤。如果重载决议选择object==object 运算符并且一个或两个操作数不是引用类型,则触发错误条件。 (这个简短的草图没有描述所有的细微之处;请参阅规范了解详细信息。)
  • @EricLippert,看到这个答案让我怀疑自己对相关问题的回答(stackoverflow.com/questions/17817979/…)。你当然看起来像是可以在这个问题上发表意见的人;甚至可能提供正确的答案
  • @Nicholas:我将在下周的 Coverity Development Testing 博客的新功能“Ask the Bug Guys”中回答这个问题。当我这样做时,我会发布一个链接。简短的回答是:没有充分的理由说明这种情况不可行;这可能是编译器或规范中的错误。
【解决方案2】:

您缺少open and closed generic types 的概念。

本质上,封闭的泛型类型是指您在泛型参数上实际指定现有类型(或者它们由编译器推断)。例如:

Nullable<int> nulInt;

开放的泛型类型是在运行时确定一个或多个泛型类型的类型(因此,Nullable&lt;T&gt; 类就是一个示例)。

【讨论】:

  • 啊,别闹了——你比我快。 :)
【解决方案3】:
  1. 第一个答案是关于方法参数的
  2. 第二个是关于泛型类型参数

这就是你所缺少的。

更准确地说: 1. C#默认是静态类型的,所以在传递参数时你会得到最合适的类型和方法。 (另请查看有关“动态”参数的答案。) 2.通过C#语法设置泛型类型参数是关于静态类型的。通过反射设置它是另外一回事。

其他: “在 .NET 中”,每种类型在运行时第一次使用时都有一个 初始化 阶段。 (参见静态字段和静态构造函数)

所以: 所有类型在运行时初始化,但静态类型在编译时使用(或动态...),此时它们需要“解析”。

【讨论】:

    【解决方案4】:

    打开类型 (myclass&lt;T&gt;) 不存在运行时。但是未绑定的类型可以在运行时存在 (myclass&lt;&gt;)。要在运行时解析未绑定的类型,您需要使用 typeof 运算符。

    换句话说,除非使用 typeof 运算符,否则泛型类型会在编译时关闭。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 2014-02-04
      相关资源
      最近更新 更多