2009-03-17 17 views
35

Me gustaría crear un objeto de diccionario, con claves de cadena, con valores que son de un tipo genérico. Me imagino que sería algo como esto:¿Puedo crear un diccionario de tipos genéricos?

Dictionary<string, List<T>> d = new Dictionary<string, List<T>>(); 

Y me permita agregar lo siguiente:

d.Add("Numbers", new List<int>()); 
d.Add("Letters", new List<string>()); 

sé que puedo hacerlo por una lista de cadenas, por ejemplo, el uso de este sintaxis:

Dictionary<string, List<string>> d = new Dictionary<string, List<string>>(); 
d.Add("Key", new List<string>()); 

pero me gustaría que lo haga por una lista genérica si es posible ...

2 preguntas luego:

  1. ¿Es posible?
  2. ¿Cuál es la sintaxis?

Muchas gracias,
Jon

Respuesta

24

EDIT: Ahora he releído la pregunta ...

No se puede hacer esto, pero una colección personalizada manejaría hasta cierto punto. Es, básicamente, tendría un genérico Add método:

public void Add<T>(string key, List<T> list) 

(. La colección en sí no sería genérico - a menos que quiera hacer el tipo de clave genérica)

No podía extracto valora de manera fuertemente tipada, porque el compilador no sabrá qué tipo ha usado para una clave en particular. Si convierte la clave en el tipo en sí, finaliza con una situación ligeramente ligeramente, pero que todavía no es compatible con las colecciones existentes. Esa es la situación a la que respondía mi respuesta original.

EDIT: Respuesta original, cuando yo no había bastante leer la pregunta correctamente, pero que puede ser de carácter informativo de todos modos ...

No, no se puede hacer un argumento de tipo depende de otra, yo soy asustado. Es solo una de las cosas que uno podría querer expresar en un sistema de tipo genérico pero que las limitaciones de .NET no permiten. Siempre habrá tales problemas, y los diseñadores de .NET optaron por mantener genéricos relativamente simple.

Sin embargo, puede escribir una colección para aplicarla con bastante facilidad. Tengo an example in a blog post que solo conserva un único valor, pero sería fácil ampliarlo para usar una lista.

+0

Ah, veo el problema ahora - no hay manera de hacer la inferencia de tipos en cualquier cosa que extraiga de la colección. Supongo que esa es la razón por la cual no está incluida como característica. ¡Gracias! –

+0

La colección en sí también puede ser genérica: usar una 'C donde T: C' sería útil. – reinierpost

17

¿Funcionaría algo así?

public class GenericDictionary 
{ 
    private Dictionary<string, object> _dict = new Dictionary<string, object>(); 

    public void Add<T>(string key, T value) where T : class 
    { 
     _dict.Add(key, value); 
    } 

    public T GetValue<T>(string key) where T : class 
    { 
     return _dict[key] as T; 
    } 
} 

Básicamente, envuelve todo el bastidor detrás de las escenas para usted.

3

Yo prefiero esta forma de poner los tipos genéricos en una colección:

interface IList 
{ 
    void Add (object item); 
} 

class MyList<T> : List<T>, IList 
{ 
    public void Add (object item) 
    { 
    base.Add ((T) item); // could put a type check here 
    } 
} 

class Program 
{ 
    static void Main (string [] args) 
    { 
    SortedDictionary<int, IList> 
     dict = new SortedDictionary<int, IList>(); 

    dict [0] = new MyList<int>(); 
    dict [1] = new MyList<float>(); 

    dict [0].Add (42); 
    dict [1].Add ("Hello"); // Fails! Type cast exception. 
    } 
} 

Pero usted pierde los controles de tipo en tiempo de compilación.

+0

Exactamente lo que estaba pensando: use un Dictionary . –

2

Estamos utilizando muchos reflejos para crear una herramienta de administración extensible. Necesitábamos una forma de registrar elementos en la búsqueda global en la definición del módulo. Cada búsqueda arrojaría resultados de manera consistente, pero cada uno tenía dependencias diferentes. He aquí un ejemplo de nosotros registrarse búsqueda de un solo módulo:

public void ConfigureSearch(ISearchConfiguration config) 
    { 
     config.AddGlobalSearchCallback<IEmploymentDataContext>((query, ctx) => 
     { 
      return ctx.Positions.Where(p => p.Name.Contains(query)).ToList().Select(p => 
       new SearchResult("Positions", p.Name, p.ThumbnailUrl, 
        new UrlContext("edit", "position", new RouteValueDictionary(new { Id = p.Id })) 
        )); 
     }); 
    } 

En el fondo durante el registro del módulo, iteramos sobre cada módulo y añadimos el Func para un SearchTable con una instancia de:

public class GenericFuncCollection : IEnumerable<Tuple<Type, Type, Object>> 
{ 
    private List<Tuple<Type, Type, Object>> objects = new List<Tuple<Type, Type, Object>>(); 

    /// <summary> 
    /// Stores a list of Func of T where T is unknown at compile time. 
    /// </summary> 
    /// <typeparam name="T1">Type of T</typeparam> 
    /// <typeparam name="T2">Type of the Func</typeparam> 
    /// <param name="func">Instance of the Func</param> 
    public void Add<T1, T2>(Object func) 
    { 
     objects.Add(new Tuple<Type, Type, Object>(typeof(T1), typeof(T2), func)); 
    } 

    public IEnumerator<Tuple<Type, Type, object>> GetEnumerator() 
    { 
     return objects.GetEnumerator(); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return objects.GetEnumerator(); 
    } 
} 

Luego, cuando finalmente lo llamamos, lo hacemos con reflejo:

var dependency = DependencyResolver.Current.GetService(search.Item1); 
var methodInfo = search.Item2.GetMethod("Invoke"); 
return (IEnumerable<SearchResult>)methodInfo.Invoke(search.Item3, new Object[] { query, dependency }); 
1

Otra posibilidad es utilizar la variable dinámica.

Por ejemplo:

Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>(); d.Add("Key", new List<dynamic>());

la variable dinámica resolver el tipo en tiempo de ejecución.

1

No encontré lo que estaba buscando aquí, pero después de leer creo que podría ser lo que se está pidiendo, así que un intento de respuesta.

El problema es que cuando usa Diccionario es un tipo construido cerrado y todos los elementos deben ser del tipo TValue. Veo esta pregunta en varios lugares sin una buena respuesta.

El hecho es que quiero indexar pero cada elemento tiene un tipo diferente y en función del valor de TKey ya conocemos el tipo. No estoy tratando de pasar el boxeo, sino simplemente tratando de obtener acceso más elegante, algo así como el campo DataSetExtensions. Y no quiero usar dinámica porque los tipos son conocidos y simplemente no se quiere.

Una solución puede ser crear un tipo no genérico que no exponga T en el nivel de clase y por lo tanto haga que la parte de TValue del diccionario se cierre construida. Luego espolvorea con un método fluido para ayudar a la inicialización.

public class GenericObject 
{ 
    private object value; 

    public T GetValue<T>() 
    { 
     return (T)value; 
    } 

    public void SetValue<T>(T value) 
    { 
     this.value = value; 
    } 

    public GenericObject WithValue<T>(T value) 
    { 
     this.value = value; 
     return this; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Dictionary<string, GenericObject> dict = new Dictionary<string, GenericObject>(); 

     dict["mystring"] = new GenericObject().WithValue<string>("Hello World"); 
     dict["myint"] = new GenericObject().WithValue<int>(1); 

     int i = dict["myint"].GetValue<int>(); 
     string s = dict["mystring"].GetValue<string>(); 
    } 
} 
0

Una de las formas es crear un valor diccionario con el tipo de "objeto" como:

Dictionary<string, object> d = new Dictionary<string, object>();

lo tanto, aquí objeto tipo de datos se utiliza como un tipo de datos genérica, se puede poner cualquier cosa en esto como un valor