Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

For C#, is it possible to specify type constraints in an instance declaration of a generic class?

Tags:

c#

types

generics

I'd like to take two objects who have a shared hierarchy, and who both implement an interface, and put them both into one container, e.g. List, that normally takes a single type argument. I've used non-compiled code in this question, because I can't find a feature of C# that lets me do what I'm asking while compiling.

So, a setup such as:

public class DataObject
{
    int property;
}

public interface IWriteData
{
    void WriteData();
}

public class A : DataObject, IWriteData, IDoSomethingElseA
public class B : DataObject, IWriteData, IDoSomethingElseB

and code that does:

var x = new A();
var y = new B();
List<T> where T : (DataObject, IWriteData) mySharedContainer = new List<T>;
T.Add(x);
T.Add(y);

The List declaration line above doesn't work, but was the closest I could think of to get at what I'm aiming for. My hunch is that this isn't something currently able to be done, and that I need to either:

  • define a shared class they can inherit from and that also implements the interface (which I'd rather not do in this case, as this isn't my code base and I'm trying to have as small a footprint as possible)
  • do runtime typecasting, and lose the benefits of compile-time type-checking
  • duplicate code by writing code that handles type A, and code that handles type B.

But, I'd be incredibly happy to find out otherwise. When thinking about it, I couldn't see any immediate reason that, theoretically, the compiler couldn't say at that line "okay, from now on I'll check everything added to this list has inherited from that class and implements these interfaces."

Thanks!

like image 517
Marshall Conover Avatar asked Feb 02 '26 12:02

Marshall Conover


2 Answers

The feature you want is called intersection types, and C# has very, very limited support for intersection types. In fact there is only one way to specify a type restriction like that, and it is this:

class C 
{
    public static void M<T>(T t) where T : DataObject, IWriteData 
    {
        List<T> myList = new List<T>() { t };
        // With this restriction we can make both these conversions:
        IEnumerable<DataObject> iedo = myList; // Legal
        IEnumerable<IWriteData> iewd = myList; // Legal 
    }
}

T is restricted to be only types that are in the intersection of the types that implement IWriteData and the types that extend DataObject.

But this doesn't solve the problem that you have. In this solution we can call M<A> or M<B> and get a list of T where T is definitely both DataObject and IWriteData. But you have to say what T is. T can be A, or B, but T cannot be "either A or B". (And "either A or B" would be a union type.)

When thinking about it, I couldn't see any immediate reason that, theoretically, the compiler couldn't say at that line "okay, from now on I'll check everything added to this list has inherited from that objectclass and implements these interfaces."

(Objects are instances of classes; classes extend classes, not objects.)

You are correct; there is no theoretical reason. There are languages that support union and intersection types; Hack, the language that I work on now, is such a language. TypeScript also supports this kind of typing. But C# is not one of them, sorry.

like image 108
Eric Lippert Avatar answered Feb 04 '26 01:02

Eric Lippert


This is not possible.

In C# you can only specify one type for a field, method, property, etc. That is why generics can not do this. Generics just compile down to separate classes/methods for every type combination you use.

You have to implement the interface in the class or inherit interfaces so that you only have to specify only one interface.

like image 21
Dennis Kuypers Avatar answered Feb 04 '26 00:02

Dennis Kuypers



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!