tipo - ¿Cómo creo una clase genérica a partir de una cadena en C#?
interfaces genericas c# (9)
Esta pregunta ya tiene una respuesta aquí:
- Convertir una cadena a un nombre de clase 2 respuestas
Tengo una clase genérica como esa:
public class Repository<T> {...}
Y necesito instanciar eso con una cadena ... Ejemplo:
string _sample = "TypeRepository";
var _rep = new Repository<sample>();
¿Cómo puedo hacer eso? ¿Es eso posible?
¡Gracias!
Aquí están mis 2 centavos:
Type genericType = typeof(Repository<>);
Type[] typeArgs = { Type.GetType("TypeRepository") };
Type repositoryType = genericType.MakeGenericType(typeArgs);
object repository = Activator.CreateInstance(repositoryType);
Respondiendo la pregunta en comentario.
MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething");
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something));
closedMethod.Invoke(repository, new[] { "Query String" });
Este es un trabajo para la palabra clave dynamic
viene en C # 4.0.
Hay algunos hacks agradables aquí que te darán una instancia de tu objeto, pero no importa lo que la versión actual de C # solo sabrá que tiene un object
, y no puede hacer mucho con un objeto solo porque el C # actual no lo hace. apoyo tarde vinculante. Necesitas poder al menos convertirlo en un tipo conocido para hacer cualquier cosa con él. En última instancia, debe saber el tipo que necesita en el momento de la compilación o no tendrá suerte.
Ahora, si puede restringir su clase de repositorio a los tipos que implementan alguna interfaz conocida, está en una forma mucho mejor.
Esto es "una especie de" posible. Algo en lo que puedes hacer, pero es un poco piratear.
Tienes 3 opciones:
Si conoce sus clases por adelantado, use una instrucción switch. Básicamente así:
switch(str){
case "TypeRepository": return new Repository<TypeRepository>;
}
Como una forma más avanzada de lo anterior, puede usar un diccionario en lugar de una tabla hash
var factory = new Dictionary<string, Func<object>>();
factory.Add( "TypeRepository", () => new Repository<TypeRepository>() );
var theObject = factory["TypeRepository"]() as Repository<TypeRepository>;
Para obtener la mayor flexibilidad, puede usar la reflexión para hacer coincidir las cadenas con las clases en tiempo de ejecución. Tenga en cuenta que la reflexión es bastante lenta, por lo que si lo hace con regularidad, querrá evitarlo. Como ejemplo, aquí hay uno que usa el método de Frans Bouma . Simplemente cambie List<>
a Repository<>
en su código:
public static object MakeList( string typeStr )
{
var nameSpace = "MyTestApplication" + ".";
var objType = Type.GetType(nameSpace + typeStr); // NAMESPACE IS REQUIRED
var listType = typeof(List<>).MakeGenericType(objType);
return Activator.CreateInstance(listType);
}
var listOfThings = MakeList("MyCoolObject") as List<MyCoolObject>;
Nota: no hay forma de evitar el hecho de que todos estos mecanismos devuelven un object
, que luego debe convertir a su tipo adecuado, en lugar de devolver solo valores fuertemente tipados.
Esto es inevitable, ya que no conoce el tipo de la lista hasta el tiempo de ejecución, y C # se basa en saber las cosas en tiempo de compilación (esto es lo que quiere decir la gente cuando dice "lenguaje de programación tipificado estáticamente"). Será menos doloroso en C # 4, donde podrás devolver la dynamic
, lo que te ahorrará el casting.
La "T" en una clase genérica representa un tipo, no una instancia de un tipo. Entonces, si desea que su repositorio contenga objetos de cadena, entonces use
var _rep = new Repository<string>();
Los genéricos funcionan en tipos, no en instancias. Puedes leer más here .
Suena como que solo necesita agregar un constructor a su clase que tome el tipo deseado y lo initailice el valor:
public class Foo<T>
{
private T = default(T);
public Foo(T initValue)
{
_val = T;
}
}
Primero obtenga el objeto Type usando Type.GetType(stringContainingTheGenericTypeArgument)
Luego use typeof(Repository<>).MakeGenericType(theTypeObject)
para obtener un tipo genérico.
Y finalmente usar Activator.CreateInstance
Si entiendo su pregunta correctamente ... ¿Qué está tratando de hacer es tomar su tipo (Repositorio <T>) y construir una implementación genérica específica en tiempo de ejecución?
Si es así, eche un vistazo a MakeGenericType . Puede usar typeof (Repository) y System.Type del objeto que desea para T y construirlo de esta manera. Una vez que tenga el tipo, Activator.CreateInstance funcionará para usted.
Suponiendo que su cadena contiene el nombre de un tipo, puede escribir
object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));
Sin embargo, _rep será un objeto sin tipo y no tendrá forma de hacer nada con él. Como no sabe qué tipo de clase genérica se encuentra en el momento de la compilación, no hay ningún tipo al que lo convierta. (A menos que Repository herede una clase no genérica o implementa una interfaz no genérica)
Podría resolverlo creando una clase base no genérica para el Repositorio y asignándole el objeto.
Sin embargo, dependiendo de su situación, es probable que desee hacer del Repository
una clase no genérica.
Si Repository
es una clase que contiene otras clases, probablemente sea mejor que sea no genérico. Podría hacer que su constructor tome un objeto Type
y luego llamar a Type.IsInstanceOfType
en cada objeto que agregue para asegurarse de que sea el tipo correcto.
Si el Repositorio es una colección categorizada de cosas, probablemente sea mejor que sea no genérico y que su constructor tome una string category
.
Si desea obtener consejos más específicos, publique más detalles sobre su situación.
Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String"));
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);
Esto crearía una instancia de Repository<string>
. Puede reemplazar "System.String" con el tipo que desee.