Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make sure Type instance represents a type assignable from certain class

I'm primarily a Java programmer, so this would be one of those "what is this thing from Java equivalent to in C#" questions. So, in Java, you can restrain a Class type argument at compile time to extend a certain super-class, like so:

public <T extends BaseClass> void foo(Class<T> type) {
    ...
}

and even

public <T extends BaseClass> T foo(Class<T> type) {
    ...
}

You can even chain multiple interfaces:

public <T extends BaseClass & BaseInterface1 & BaseInterface2> void foo(Class<T> type) {
    ...
}

How is this done in C#? I know you can use "where T : BaseClass", but this is only applicable when you have an instance T. What about when you only have a Type instance?

EDIT:

For explanation, here is what I would like to do:

ASSEMBLY #1 (base.dll):

abstract class BaseClass {
    abstract void Foo();
}

ASSEMBLY #2 (sub1.dll, references base.dll):

class SubClass1 : BaseClass {
    void Foo() {
        // some code
    }
}

ASSEMBLY #3 (sub2.dll, references base.dll):

class SubClass2 : BaseClass {
    void Foo() {
        // some other code
    }
}

ASSEMBLY #4 (main.dll, references base.dll):

class BaseClassUtil {
    static void CallFoo(Type<T> type) where T : BaseClass {
        T instance = (T)Activator.CreateInstance(type);
        instance.Foo();
    }
}

public static void Main(String[] args) {
    // Here I use 'args' to get a class type,
    // possibly loading it dynamically from a DLL

    Type<? : BaseClass> type = LoadFromDll(args); // Loaded from DLL

    BaseClassUtil.CallFoo(type);
}

So, in this example, I don't care what class the 'type' variable represents, as long as it is derived from BaseClass, so once I create an instance, can call Foo().

The parts that are not vaild C# code (but rather some Java mockup) are the "generic" Type classes: Type<T> and Type<? : BaseClass>.

like image 549
milin Avatar asked Dec 12 '16 13:12

milin


2 Answers

No, there is no way to enforce at compile time that a Type be assignable to a generic type. If I understand correctly, what you want is:

 void Foo<T>(Type type) { ... } //compile time error if an instace typed `type` is not assignable to `T`.

Which means:

 void Foo<IFormattable>(typeof(string)); //ok
 void Foo<IDisposable>(typeof(string)); //compile time error

Evidently at runtime it is trival, but the language has no support for this at compile time.

like image 143
InBetween Avatar answered Nov 09 '22 02:11

InBetween


From what I understood you are talking about generic type constraint

public void Foo<T>(Type type) where T:BaseClass, BaseInterface1, BaseInterface2
{
    //your code
}

Here another article:Constraints on Type Parameters (C# Programming Guide)

When you define a generic class, you can apply restrictions to the kinds of types that client code can use for type arguments when it instantiates your class. If client code tries to instantiate your class by using a type that is not allowed by a constraint, the result is a compile-time error.

EDIT:

Here your example. Now if you try to call BaseClassUtil.CallFoo<T> with something different from BaseClass and his derived classes you will receive an compile error. Here full example in dotNetFiddle. So the tricky part is the restriction of your class should happen in the Util class

    public static void Main(string[] args)
    {
        //so your LoadFromDll method should return Type. Type doesn't have generic implementation !
        Type type = typeof(SubClass1);

        BaseClassUtil.CallFoo<BaseClass>(type);

        Type type2 = typeof(SubClass2);
        //you can write BaseClassUtil.CallFoo<SubClass2>(type2); if you want
        BaseClassUtil.CallFoo<BaseClass>(type2);
    }

    public class BaseClassUtil
    {
        public static void CallFoo<T>(Type type) where T : BaseClass
        {
            T instance = (T)Activator.CreateInstance(type);
            instance.Foo();
        }
    }
    public class TestClass
    {
        public int ID { get; set; }

    }

    public abstract class BaseClass
    {
        public abstract void Foo();
    }

    public class SubClass1 : BaseClass
    {
        public override void Foo()
        {
            Console.WriteLine("SubClass 1");
        }
    }

    public class SubClass2 : BaseClass
    {
        public override void Foo()
        {
            Console.WriteLine("SubClass 2");
        }

    }
like image 34
mybirthname Avatar answered Nov 09 '22 03:11

mybirthname