Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 938
Tomas Aschan Avatar asked Jun 05 '11 17:06

Tomas Aschan


People also ask

Can interface be a reference type?

When you define a new interface, you are defining a new reference data type. You can use interface names anywhere you can use any other data type name. If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface.

Are interfaces reference types C#?

In C#, classes and interfaces are reference types. Variables of reference types store references to their data (objects) in memory, and they do not contain the data itself.

Is an interface a value type or reference type?

Interfaces are implemented by types, and those types are either value types or reference types. Obviously, both int and string implement IComparable , and int is a value type, and string is a reference type.

CAN interface have implemented methods C#?

With C# 8.0, you can now have default implementations of methods in an interface. Interface members can be private, protected, and static as well. Protected members of an interface cannot be accessed in the class that extends the interface. Rather, they can be accessed only in the derived interface.


1 Answers

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.

like image 63
Joel B Fant Avatar answered Oct 07 '22 09:10

Joel B Fant