Если вы передаете что-то под интерфейсом, то даже если у вас есть тип значения, реализующий этот интерфейс, он будет упакован, если приведен к интерфейсу, и будет вести себя как ссылочный тип (потому что он заключен в рамку внутри ссылочного типа).
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
, и тогда может не иметь значения, реализует ли интерфейс интерфейс, пока он в штучной упаковке. Однако, зависит от того, что делает проверка EF, если передается в штучной упаковке. Может быть, это сработает.
Если это не сработает, по крайней мере, общее ограничение приведет вас туда-сюда, и вы можете проверить foo.GetType().IsValueType
(ссылаясь на мой метод DoSomething
выше) и бросить ArgumentException
для обработки случая в штучной упаковке.