是“T:class”在编译时或运行时是否以任何方式强制执行?

时间:2012-09-21 19:57:25

标签: c# class generics struct

在下面的代码中,我将一个struct传递给一个期望一个类的构造函数。为什么编译运行没有错误(并产生所需的输出)?

class Program
{
    static void Main()
    {
        var entity = new Foo { Id = 3 };
        var t = new Test<IEntity>(entity); // why doesn't this fail?
        Console.WriteLine(t.Entity.Id.ToString());
        Console.ReadKey();
    }
}

public class Test<TEntity> where TEntity : class
{
    public TEntity Entity { get; set; }

    public Test(TEntity entity)
    {
        Entity = entity;
    }

    public void ClearEntity()
    {
        Entity = null;
    }
}

public struct Foo : IEntity
{
    public int Id { get; set; }
}

public interface IEntity
{
    int Id { get; set; }
}

如果我更改了我的Main()方法,使其包含对ClearEntity()的调用,如下所示,仍然不会产生任何错误。为什么呢?

static void Main()
{
    var entity = new Foo { Id = 3 };
    var t = new Test<IEntity>(entity);
    Console.WriteLine(t.Entity.Id.ToString());
    t.ClearEntity(); // why doesn't this fail?
    Console.ReadKey();
}

4 个答案:

答案 0 :(得分:8)

where TEntity : class强制TEntity成为引用类型,但IEntity等接口是引用类型。

见这里: http://msdn.microsoft.com/en-us/library/d5x73970(v=vs.80).aspx

其中T:class | type参数必须是引用类型,包括任何类,接口,委托或数组类型

关于你的第二个问题,你可能认为t.ClearEntity()会失败,因为它为类型为值类型的变量赋值null,但事实并非如此。编译时类型Entity是引用类型IEntity,运行时类型(赋值后)是null类型。所以你永远不会有Foo类型的变量,而是值null

答案 1 :(得分:2)

来自C#文档:

  

其中T:class

     

type参数必须是引用类型,包括任何类,接口,委托或数组类型。 (见下面的注释。)

因为您通过接口传递结构,所以它仍被视为引用类型。

答案 2 :(得分:1)

在.net运行时中,每个非可空值类型都有一个关联的引用类型(通常称为“盒装值类型”),它派生自System.ValueType。说Object Foo = 5;实际上不会将Int32存储到Foo;相反,它将创建与Int32关联的引用类型的新实例,并存储对该实例的引用。泛型类型的class约束指定所讨论的类型必须是某种类型的引用类型,但本身并不排除该类型可用于将引用传递给盒装值类型的可能性实例。在泛型类型约束之外的大多数上下文中,接口类型被视为类类型。

值得注意的是,不仅盒装值类型存储为引用类型;它们的行为类似于引用类型。例如,List<string>.Enumerator是实现IEnumerator<string>的值类型。如果有两个类型为List<string>.Enumerator的变量,则将一个变量复制到另一个变量将复制枚举的状态,这样就会有两个独立且独立的枚举器指向同一个列表。将其中一个变量复制到IEnumerator<string>类型的变量将创建与List<string.Enumerator关联的盒装值类型的新实例,并在后一个变量中存储对该新对象的引用(将是第三个独立调查员)。但是,将该变量复制到另一个IEnumerator<string>类型的变量只会存储对现有对象的引用(因为IEnumerator<string>是引用类型)。

C#语言试图假装值类型派生自Object,但在.net运行时的内容中,它们实际上并非如此。相反,它们是可转换到源自System.ValueType的类型(后者又来自Object)。后者类型将满足类型约束,即使前者不会。顺便提一下,尽管它的名字,System.ValueType实际上是一个类类型。

答案 3 :(得分:0)

我同样假设约束关键字class与类型声明关键字class意味着相同的类,但它没有。

正如其他答案所解释的那样,这里的术语class是过载的,在我看来,这对C#语言设计来说是一个可怕的决定。像referencetype之类的东西会更有帮助。