Make sure Type instance represents a type assignable from certain class
Asked Answered
B

2

9

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

Broder answered 12/12, 2016 at 13:1 Comment(0)
T
2

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.

Telfer answered 12/12, 2016 at 14:12 Comment(0)
O
2

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");
        }

    }
Obstreperous answered 12/12, 2016 at 13:12 Comment(7)
Thank you for your answer, but I'm not sure it fits my needs. The problematic part is the "Class<T> type" part - I don't think that's valid C# syntax - and that's the whole issue. If it was, then this would definitely solve my problem. But I'm pretty sure it's not...Broder
@Broder I wrote you little example I hope this clears your doubtsObstreperous
I added an example what I would like to do - I feel my explanation was not clear enough because your example is concerned about generic instances, and I have a problem with generic Type instances.Broder
@Broder I don't have your method LoadFromDll, but this method should return Type. Type doesn't have generic implementation. I made your code to work.Obstreperous
And what does your code do if I call BaseClassUtil.CallFoo(typeof(String))? Also, I don't know of the class "SubClass2" at compile time, so I can't write "BaseClassUtil.CallFoo<SubClass2>(type2)".Broder
@Broder you will call it like this BaseClassUtil.CallFoo<string>(typeof(String)); and the compiler will give you and error: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Program.BaseClassUtil.CallFoo<T>(Type)'. There is no implicit reference conversion from 'string' to 'ConsoleApplication1.Program.BaseClass'.Obstreperous
Let us continue this discussion in chat.Broder
T
2

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.

Telfer answered 12/12, 2016 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.