简体   繁体   中英

c# Generics. <T>, programmatically assigning T from reading a string value?

I have two Classes (ie Customer , and Employee ) and a generic repository GenericRepository<T> where T : class .

Is is possible to instantiate a new GenericRepository instance while assigning the value of T from a string?

Like this:

string x  = "Customer";
var repository = new GenericRepository<x>();

(thus creating a repository instance of type GenericRepository<Customer> )

Yes, but it's very awkward.

string name = "MyNamespace.Customer";

Type targetType = Type.GetType(name);

Type genericType = typeof(GenericRepository<>).MakeGenericType( targetType );

object instance = Activator.CreateInstance(genericType);

In linqpad, instance.Dump(); :

GenericRepository<Customer> 
UserQuery+GenericRepository`1[UserQuery+Customer] 

Edit

You could assign the CreateInstance result to a dynamic, and not have to invoke methods through reflection.

dynamic instance = Activator.CreateInstance(genericType);
instance.SomeInstanceMethod(someParameter);

You can use reflection to create a generic object, for example, you can use a string to find a type (using System.Type.GetType). Once you have a type object, you can use it to find a constructor from the reflection API - see

http://msdn.microsoft.com/en-us/library/ms172334.aspx

Sure you can. You'll probably want an interface to your Repository as well. This serves two purposes. Firstly, you can call the methods in a type-safe language bound manner rather than having to dynamically invoke, and second it'll allow you to mock it easily for testing.

using System;
using System.Collections.Generic;

namespace QuickTest
{
    public interface IRepository
    {
        void test();
    }
    public class GenericRepositoryFactory
    {
        static public IRepository CreateInstance(params Type[] p)
        {
            Type genericType = typeof(GenericRepository<>).MakeGenericType(p);
            return Activator.CreateInstance(genericType) as IRepository;
        }
    }

    public class GenericRepository<t> : IRepository
    {
        public void test() { Console.WriteLine(this.GetType().ToString()); }
    }

    class Program
    {
        public static void Main(string[] argv)
        {
            var repo = GenericRepositoryFactory.CreateInstance(new[] { typeof(string) });
            repo.test();
        }
    }
}

You can't do it as directly as your example would like it to be, but you can implement a Factory pattern to help you out here. Just pass the string into your factory to return the repository.

EDIT: The factory would have a dictionary of all of the classes that are in the repository (ie Customer and Employee) that would get loaded at instantiation. I recommend using attributes and reflection so that you can populate the dictionary dynamically. You then have a method that takes in the string that returns something of type GenericRepository where the string corresponds to one of the types in the dictionary. You then create an instance of that GenericRepository and return it to your caller.

You can use Type.GetType(string) to get a type definition of "Customer". It generally requires at least the full namespace when calling it. Then you can use the type in a call to Type.MakeGenericType(params Type[]) to create a type definition of the generic type. Finally a call to Invoke(object[]) on your newly created type should create an instance of this.

using System;

class Foo<T> where T : class { }

class Bar { }

class Program {
    static void Main(string[] args) {
        var barType = Type.GetType("ConsoleApplication29.Bar");
        var fooType = typeof(Foo<>).MakeGenericType(barType);
        var fooCtor = fooType.GetConstructor(Type.EmptyTypes);
        var instance = fooCtor.Invoke(new object[] { });
        Console.WriteLine(instance.GetType().FullName);
        Console.ReadLine();
    }
}

Yes, it is possible. Sample code below, however I would recommend a factory pattern instead.

    internal class Program {

        private static void Main() {

            string customerString = @"Customer";
            string employeeString = @"Employee";

            object customerRepository = GetGenericRepository(customerString);
            object employeeRepository = GetGenericRepository(employeeString);

            System.Console.WriteLine();
        }

        public static object GetGenericRepository(string typeName) {

            // get the type from the string
            System.Type type = GetTypeFromName(typeName);

            System.Type repositoryOpenType = typeof(GenericRepository<>);
            System.Type repositoryClosedType = repositoryOpenType.MakeGenericType(type);

            return System.Activator.CreateInstance(repositoryClosedType);
        }

        // there are better methods for getting the type by name
        private static System.Type GetTypeFromName(string typeName) {

            System.Type type = System.Type.GetType(typeName, false);

            if (type == null) {

                var types = from assemblyType in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                            where assemblyType.Name == typeName
                            select assemblyType;

                type = types.FirstOrDefault();
            }

            return type;
        }
    }

    public class Customer {
    }

    public class Employee {
    }

    public class GenericRepository<T> where T : class {
    }
}

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