如果你在一个接口下传递一些东西,那么即使你有一个实现该接口的值类型,如果强制转换为接口,它也会被装箱并且表现得像一个引用类型(因为它被装箱在一个引用类型中)。
interface IFoo {
int Value { get; set; }
}
struct Foo : IFoo {
public int Value { get; set; }
}
观察用作值类型时的效果:
var a = new Foo() { Value = 3 };
var b = a; // copies value
b.Value = 4;
Console.WriteLine( "a has {0}", a.Value ); //output: a has 3
Console.WriteLine( "b has {0}", b.Value ); //output: b has 4
现在看看当你将它转换到接口时会发生什么:
var a = new Foo() { Value = 3 } as IFoo; //boxed
var b = a; // copies reference
b.Value = 4;
Console.WriteLine( "a has {0}", a.Value ); //output: a has 4
Console.WriteLine( "b has {0}", b.Value ); //output: b has 4
因此,结构或类是否实现接口并不重要。如果强制转换为接口,然后在接口下传递,那么它将表现为引用类型。
编辑:所以如果这些是你的要求......
对于合同 X:
- 如果结构实现/继承 X,则引发编译错误。
- X 可能不是抽象类。
好吧,你只是被卡住了,因为它们相互矛盾。
- 如果结构实现/继承合同,则获得编译错误的唯一方法是它是一个抽象类。
- 由于您不能使用抽象类来保持继承选项打开,因此您必须使用接口。
- 强制执行结构无法实现接口的规则的唯一方法是在运行时。
使用约束where T: class, IFoo
甚至不会一直有效。如果我有这种方法(基于相同Foo
和IFoo
以上):
static void DoSomething<T>(T foo) where T: class, IFoo {
foo.Value += 1;
Console.WriteLine( "foo has {0}", foo.Value );
}
那么在这种情况下它会抛出一个编译错误:
var a = new Foo(){ Value = 3 };
DoSomething(a);
但在这种情况下它会工作得很好:
var a = new Foo(){ Value = 3} as IFoo; //boxed
DoSomething(a);
因此,就我而言,使用where T: class, IFoo
-style 约束,然后结构是否实现接口可能无关紧要,只要它被装箱即可。不过,取决于如果通过盒装结构检查 EF 会做什么。也许它会起作用。
如果它不起作用,至少通用约束可以让你部分到达那里,你可以检查foo.GetType().IsValueType
(参考我DoSomething
上面的方法)并抛出一个ArgumentException
来处理盒装结构的情况。