简体   繁体   中英

Specify that an interface can only be implemented by reference types C#

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?

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:

  1. Throw a compile error if a struct implements/inherits X.
  2. 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.

http://msdn.microsoft.com/en-us/library/d5x73970.aspx . Looks like you can specify it's a "class", which means reference type.

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:/

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 (eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM