Pass An Instantiated System.Type as a Type Parameter for a Generic Class

后端 未结 6 1170
萌比男神i
萌比男神i 2020-11-21 07:46

The title is kind of obscure. What I want to know is if this is possible:

string typeName = ;
Type myType = Type.GetType(         


        
相关标签:
6条回答
  • 2020-11-21 08:02

    Some additional how to run with scissors code. Suppose you have a class similar to

    public class Encoder() {
    public void Markdown(IEnumerable<FooContent> contents) { do magic }
    public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
    }
    

    Suppose at runtime you have a FooContent

    If you were able to bind at compile time you would want

    var fooContents = new List<FooContent>(fooContent)
    new Encoder().Markdown(fooContents)
    

    However you cannot do this at runtime. To do this at runtime you would do along the lines of:

    var listType = typeof(List<>).MakeGenericType(myType);
    var dynamicList = Activator.CreateInstance(listType);
    ((IList)dynamicList).Add(fooContent);
    

    To dynamically invoke Markdown(IEnumerable<FooContent> contents)

    new Encoder().Markdown( (dynamic) dynamicList)
    

    Note the usage of dynamic in the method call. At runtime dynamicList will be List<FooContent> (additionally also being IEnumerable<FooContent>) since even usage of dynamic is still rooted to a strongly typed language the run time binder will select the appropriate Markdown method. If there is no exact type matches, it will look for an object parameter method and if neither match a runtime binder exception will be raised alerting that no method matches.

    The obvious draw back to this approach is a huge loss of type safety at compile time. Nevertheless code along these lines will let you operate in a very dynamic sense that at runtime is still fully typed as you expect it to be.

    0 讨论(0)
  • 2020-11-21 08:02

    My requirements were slightly different, but will hopefully help someone. I needed to read type from a config and instantiate the generic type dynamically.

    namespace GenericTest
    {
        public class Item
        {
        }
    }
    
    namespace GenericTest
    {
        public class GenericClass<T>
        {
        }
    }
    

    Finally, here is how you call it. Define the type with a backtick.

    var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
    var a = Activator.CreateInstance(t);
    
    0 讨论(0)
  • 2020-11-21 08:03

    If you know what types will be passed you can do this without reflection. A switch statement would work. Obviously, this would only work in a limited number of cases, but it'll be much faster than reflection.

    public class Type1 { }
    
    public class Type2 { }
    
    public class Generic<T> { }
    
    public class Program
    {
        public static void Main()
        {
            var typeName = nameof(Type1);
    
            switch (typeName)
            {
                case nameof(Type1):
                    var type1 = new Generic<Type1>();
                    // do something
                    break;
                case nameof(Type2):
                    var type2 = new Generic<Type2>();
                    // do something
                    break;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 08:07

    Unfortunately no there is not. Generic arguments must be resolvable at Compile time as either 1) a valid type or 2) another generic parameter. There is no way to create generic instances based on runtime values without the big hammer of using reflection.

    0 讨论(0)
  • 2020-11-21 08:08

    You can't do this without reflection. However, you can do it with reflection. Here's a complete example:

    using System;
    using System.Reflection;
    
    public class Generic<T>
    {
        public Generic()
        {
            Console.WriteLine("T={0}", typeof(T));
        }
    }
    
    class Test
    {
        static void Main()
        {
            string typeName = "System.String";
            Type typeArgument = Type.GetType(typeName);
    
            Type genericClass = typeof(Generic<>);
            // MakeGenericType is badly named
            Type constructedClass = genericClass.MakeGenericType(typeArgument);
    
            object created = Activator.CreateInstance(constructedClass);
        }
    }
    

    Note: if your generic class accepts multiple types, you must include the commas when you omit the type names, for example:

    Type genericClass = typeof(IReadOnlyDictionary<,>);
    Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);
    
    0 讨论(0)
  • 2020-11-21 08:12

    In this snippet I want to show how to create and use a dynamically created list. For example, I'm adding to the dynamic list here.

    void AddValue<T>(object targetList, T valueToAdd)
    {
        var addMethod = targetList.GetType().GetMethod("Add");
        addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
    }
    
    var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
    var list = Activator.CreateInstance(listType);
    
    AddValue(list, 5);
    

    Similarly you can invoke any other method on the list.

    0 讨论(0)
提交回复
热议问题