问题
If I declare an interface in C#, is there any way I can explicitly declare that any type implementing that interface is a reference type?
The reason I want to do this is so that wherever I use the interface as a type parameter, I don't have to specify that the implementing type also has to be a reference type.
Example of what I want to accomplish:
public interface IInterface
{
void A();
int B { get; }
}
public class UsingType<T> where T : IInterface
{
public void DoSomething(T input)
{
SomeClass.AnotherRoutine(input);
}
}
public class SomeClass
{
public static void AnotherRoutine<T>(T input)
where T : class
{
// Do whatever...
}
}
As the argument to SomeClass.AnotherRoutine()
is required to be a reference type, I will here get a compiler error where I call the method, suggesting that I force T
to be a reference type (where T : IInterface, class
in the declaration of UsingType
). Is there any way I can enforce this already at the interface level?
public interface IInterface : class
doesn't work (obviously) but maybe there's another way to accomplish the same thing?
回答1:
If you are passing something around under an interface, then even if you have a value type implementing that interface it will become boxed if cast to the interface and behave like a reference type (because it is boxed inside a reference type).
interface IFoo {
int Value { get; set; }
}
struct Foo : IFoo {
public int Value { get; set; }
}
Observe the effects when used as a value type:
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
Now look what happens when you cast it to the interface:
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
So it doesn't matter whether a struct or class implements the interface. If cast to the interface and then is passed around under the interface, then it will behave as a reference type.
Edit: So if these are your requirements...
For contract X:
- Throw a compile error if a struct implements/inherits X.
- X may not be an abstract class.
Well, you're simply stuck then, because those contradict each other.
- The only way to get a compile error if the struct implements/inherits the contract is if it is an abstract class.
- Since you can't use an abstract class in order to keep inheritance options open, you have to use an interface.
- The only ways to enforce the rule that a struct cannot implement the interface will be during run-time.
Using the constraint where T: class, IFoo
wouldn't even work all the time. If I had this method (based on the same Foo
and IFoo
above):
static void DoSomething<T>(T foo) where T: class, IFoo {
foo.Value += 1;
Console.WriteLine( "foo has {0}", foo.Value );
}
Then it would throw a compile error under this circumstance:
var a = new Foo(){ Value = 3 };
DoSomething(a);
But it would work just fine under this circumstance:
var a = new Foo(){ Value = 3} as IFoo; //boxed
DoSomething(a);
So as far as I'm concerned, use where T: class, IFoo
-style constraint, and then it may not matter if a struct implements the interface as long as it is boxed. Depends on what checking EF does if passed a boxed struct, though. Maybe it will work.
If it doesn't work, at least the generic constraint gets you part-way there, and you can check foo.GetType().IsValueType
(referring to my DoSomething
method above) and throw an ArgumentException
to handle the case of boxed structs.
回答2:
http://msdn.microsoft.com/en-us/library/d5x73970.aspx. Looks like you can specify it's a "class", which means reference type.
回答3:
i dont think you can restrict interfaces in that way unfortunatly. According to msdn interfaces can be implemented by any type, both structs and classes :/
回答4:
The boxing behavior of mutable structs cast as interfaces can certainly be annoying. I don't know that a prohibition on all structs would be necessary, though. There are a number of cases where it may be useful to have an immutable struct wrapping a class object (e.g. one way of implementing something like a Dictionary which supports multiple ways of enumeration would have been to have Dictionary.Keys and Dictionary.Values both be structures, each holding a reference to a Dictionary, and each providing a special GetEnumerator method; Dictionary isn't implemented that way, but it's not a bad pattern). Such a pattern may avoid boxing in cases where one calls GetEnumerator on the object directly (as vb.net and c# both do with their duck-typed foreach loops); because the structures are immutable, even when boxing does occur it's a performance issue rather than a correctness one.
来源:https://stackoverflow.com/questions/6244680/specify-that-an-interface-can-only-be-implemented-by-reference-types-c-sharp