2009-03-26 12 views
15

tengo una clase genérica de esa manera:¿Cómo creo una clase genérica a partir de una cadena en C#?

public class Repository<T> {...} 

Y necesito ejemplo, que con una cadena ... Ejemplo:

string _sample = "TypeRepository"; 
var _rep = new Repository<sample>(); 

¿Cómo puedo hacer eso? ¿Es eso posible?

Gracias!

+0

Tenga en cuenta que no puede consultar el Repositorio en su programa, por lo que el tipo de _rep deberá ser 'object' o alguna interfaz común a todos los Repository 's. –

Respuesta

24

Aquí es mi 2 centavos:

Type genericType = typeof(Repository<>); 
Type[] typeArgs = { Type.GetType("TypeRepository") }; 
Type repositoryType = genericType.MakeGenericType(typeArgs); 

object repository = Activator.CreateInstance(repositoryType); 

Respondiendo a la pregunta en el comentario.

MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething"); 
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something)); 
closedMethod.Invoke(repository, new[] { "Query String" }); 
+0

¿Y cómo puedo acceder a los métodos de repositorio? – Paul

+2

De la misma forma que ha creado el repositorio para el tipo que solo se conoce en tiempo de ejecución, utilizando la reflexión. Desea flexibilidad, necesita pagarla. – alex

+0

¿Me puede mostrar cómo hacerlo? Gracias! – Paul

-3

La "T" en una clase genérica representa un tipo, no una instancia de un tipo. Así que si desea que su repositorio para almacenar objetos de cadena, a continuación, utilizar

var _rep = new Repository<string>(); 
+0

Creo que ha malentendido la pregunta: la cadena contiene un nombre de tipo. –

16

En primer lugar obtener el objeto Tipo usando Type.GetType(stringContainingTheGenericTypeArgument)

A continuación, utilice typeof(Repository<>).MakeGenericType(theTypeObject) para obtener un tipo genérico.

Y, por último usar Activator.CreateInstance

+0

¿No es la verdadera pregunta "debería él"? –

+0

de cadena a tipo genérico? Quizás. Sin embargo, MakeGenericType a menudo se usa en proveedores de linq, y en otros códigos que consumen árboles de expresiones, por ejemplo.es una gran ayuda, aunque para las aplicaciones LOB diarias no es algo de lo que preocuparse, por supuesto. –

0

Ésta es "una especie de" posible. Algo así como que puedes hacerlo, pero es un poco complicado.

usted tiene 3 opciones:

Si conoces a sus clases en la delantera, utilizar una sentencia switch. Básicamente así:

switch(str){ 
    case "TypeRepository": return new Repository<TypeRepository>; 
} 

Como una forma más avanzada de lo anterior, se 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 mayor flexibilidad, puede utilizar la reflexión para que coincida con cadenas a clases en tiempo de ejecución. Tenga en cuenta que la reflexión es bastante lenta, por lo que si está haciendo esto con cualquier tipo de regularidad, quiere evitarlo. Como ejemplo, Aquí está uno que usa el método Frans Bouma's. Sólo cambia List<> a Repository<> en el 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 de retorno object, que luego tiene que enviar contenido al tipo adecuado, en lugar de volver acaban de volver fuertemente tipado valores.

Esto es inevitable, porque no conoce el tipo de la lista hasta el tiempo de ejecución, y C# se basa en saber cosas en tiempo de compilación (esto es lo que las personas quieren decir cuando dicen "lenguaje de programación tipado estáticamente"). Será menos doloroso en C# 4, donde podrá devolver dynamic, lo que le ahorrará el lanzamiento.

+0

Dado que el tipo se está construyendo utilizando una cadena, ¿cómo podría C# asociar razonablemente un tipo específico con el resultado? –

0

genéricos funcionan sobre los tipos, no en las instancias. Puede leer más here.

suena como usted sólo tiene que añadir un constructor para la clase que lleva en el tipo deseado y el valor initailizes:

public class Foo<T> 
{ 
    private T = default(T); 

    public Foo(T initValue) 
    { 
     _val = T; 
    } 
} 
2

Si entiendo bien su pregunta ... Lo que usted está tratando de hacer es tomar su tipo (Repositorio <T>) y construir una aplicación específica, genérica de que en tiempo de ejecución?

Si es así, échele 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.

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

2

Suponiendo que la cadena tiene el nombre de un tipo, se puede escribir

object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample))); 

Sin embargo, _rep habrá un objeto sin tipo, y usted no tendrá ninguna manera de hacer nada con ella. Como no sabe de qué tipo es la clase genérica en tiempo de compilación, no hay ningún tipo en el que la genere. (A menos que el repositorio herede una clase no genérica o implemente una interfaz no genérica)

Puede resolver eso haciendo una clase base no genérica para Repository y transfiriéndole el objeto.

Sin embargo, dependiendo de su situación, es probable que desee hacer Repository una clase no genérica.

Si Repository es una clase que contiene otras clases, probablemente sea mejor hacerlo no genérico. Puede hacer que su constructor tome un objeto Type, y luego llame al Type.IsInstanceOfType en cada objeto que agregue para asegurarse de que sea del tipo correcto.

Si el repositorio es una colección categorizada de cosas, probablemente sea mejor hacerlo no genérico, y hacer que su constructor tome un string category.

Si desea obtener más información específica, publique más detalles sobre su situación.

+0

Mi Repositorio: public class Repositorio : RepositoryWithTypedId , IRepository {} clase pública RepositoryWithTypedId : IRepositoryWithTypedId y no puedo cambiar eso implementantion ... Entonces, ¿es imposible? – Paul

+0

Es posible, usando la línea de código que escribí arriba, pero sería muy difícil hacer algo con. ¿Por qué estás tratando de hacer esto, y qué estás haciendo con '_rep'? – SLaks

+0

Mi _rep es un DAO (Objeto de acceso a datos) ... – Paul

3

Este es un trabajo para la palabra clave dynamic que viene en C# 4.0.

Hay algunos buenos hacks aquí que le darán una instancia de su objeto, pero no importa cuál sea la versión actual de C# solo sabrá que tiene un object, y no puede hacer mucho con un objeto en sí mismo porque C# actual no admite el enlace tardío. Necesita poder, al menos, convertirlo en un tipo conocido para hacer algo con él. En última instancia, debe saber el tipo que necesita en tiempo de compilación o no tiene suerte.

Ahora, si puede limitar su clase de repositorio a los tipos que implementan alguna interfaz conocida, se encuentra en una forma mucho mejor.

+0

Entonces, no tengo una solución para mi problema ... – Paul

+0

No, y mi punto es que no obtendrás uno que realmente funcione antes de que C# 4 salga a menos que puedas volver a pensar tu arquitectura. –

+2

Si comparte lo que piensa hacer con estos objetos de repositorio, podemos ayudarle con un diseño que funcione mejor. –

Cuestiones relacionadas