6

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

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

4 回答 4

8

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

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

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

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

于 2012-09-21T20:02:48.160 回答
2

来自 C# 文档:

其中 T :类

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

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

于 2012-09-21T20:03:09.630 回答
1

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

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

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

于 2012-09-21T20:59:38.383 回答
0

同样,我假设约束关键字class与类型声明关键字表示相同的类class,但事实并非如此。

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

于 2013-10-19T23:18:04.980 回答