简体   繁体   中英

Using reflection to create a class that implements an interface of type T

I have a strange reflection problem.

Refer to the class FooAttempt1 that implements the interface IFooAttempt1 which has a property that returns an IAnimal . In the case of FooAttempt1 it is returning a Dog .

In the program in question, our classes that implement IFooAttempt1 are instantiated, do some work and then store their own reflected type and a key to the database and are later rehydrated using reflection. There are many such classes that implement IFooAttempt1 and it is an architecture that is working quite well.

You can see the basic process at work in the method Attempt1() .

The problem is that that real classes are slightly more complex than IFooAttempt1 and it is easy to make a mistake. As such we have refactored the code slightly to use generics to help ensure that these classes are implemented correctly.

Refer to FooAttempt2 which implements a generic interface IFooAttempt2 < T > where T : IAnimal .

This ensures that the Animal property is now defined as

public Dog Animal

rather than

public IAnimal Animal

Both IFooAttempt1 and IFooAttempt2 are functionally identical but this self-documenting feature of generics makes implementing our rather more complex classes very simple.

The problem comes when you refer to the Attempt2() code. When using reflection to recreate FooAttempt2 the line

object o = constructor.Invoke(null);
IFooAttempt2<IAnimal> foo2 = (IFooAttempt2<IAnimal>) o;

displays the error

An unhandled exception of type 'System.InvalidCastException' occurred in 
ConsoleApplication1.exe Additional information: Unable to cast object of 
type 'ConsoleApplication1.FooAttempt2' to type 'ConsoleApplication1.IFooAttempt2`1
[ConsoleApplication1.IAnimal]'.

I can understand its point but how do you do this using reflection?

In the immediate window, if you go

? o
{ConsoleApplication1.FooAttempt2}
Animal: {ConsoleApplication1.Dog}

so it has reflected correctly. I just can't cast it to a ( IFooAttempt2<IAnimal> ) even though FooAttempt2 is a defined as IFooAttempt2<Dog> where Dog is an IAnimal .

My immediate thought was to try to do this

Type type = assembly.GetType(className, false, true).MakeGenericType(new[] { typeof(IAnimal) });

but it does not compile. Evidently the compiler does not like the typeof(interface)

At this stage I am totally stuck.

Thanks!

class Program
{
    static void Main(string[] args)
    {
        Attempt1();
        Attempt2();
    }


    static void Attempt1()
    {
        FooAttempt1 foo = new FooAttempt1();
        Console.WriteLine(foo.Animal.MakeNoise());

        string className = foo.GetType().FullName;

        // -----

        // get the assembly
        Assembly assembly = typeof(FooAttempt1).Assembly;

        // get the class
        Type type = assembly.GetType(className, false, true);

        // create an instance
        ConstructorInfo constructor = type.GetConstructor(new Type[] { });

        IFooAttempt1 foo2 = (IFooAttempt1)constructor.Invoke(null);
        Console.WriteLine(foo2.Animal.MakeNoise());

    }


    static void Attempt2()
    {
        FooAttempt2 foo = new FooAttempt2();
        Console.WriteLine(foo.Animal.MakeNoise());

        string className = foo.GetType().FullName;

        // -----

        // get the assembly
        Assembly assembly = typeof(FooAttempt2).Assembly;

        // get the class
        Type type = assembly.GetType(className, false, true);

        // create an instance
        ConstructorInfo constructor = type.GetConstructor(new Type[] { });

        object o = constructor.Invoke(null);
        IFooAttempt2<IAnimal> foo2 = (IFooAttempt2<IAnimal>) o;  // << PROBLEM HERE
        Console.WriteLine(foo2.Animal.MakeNoise());
    }
}


public interface IAnimal
{
    string MakeNoise();
}

public class Dog : IAnimal
{
    public string MakeNoise()
    {
        return "Bark";
    }
}

public interface IFooAttempt1
{
    IAnimal Animal
    { 
        get; 
    }
}

public class FooAttempt1 : IFooAttempt1
{
    public FooAttempt1()
    {
    }

    public IAnimal Animal
    {
        get 
        { 
            return new Dog(); 
        }
    }
}


public interface IFooAttempt2<T>
    where T : IAnimal 
{
    T Animal
    { 
        get; 
    }
}

public class FooAttempt2 : IFooAttempt2<Dog>
{
    public FooAttempt2()
    {
    }

    public Dog Animal
    {
        get
        {
            return new Dog();
        }
    }
}

You can make IFooAttempt2 covariant in T :

public interface IFooAttempt2<out T>
    where T : IAnimal 
{
    T Animal
    { 
        get; 
    }
}

then the cast to IFooAttempt2<IAnimal> will succeed.

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