Diccionario de tipo seguro para varios tipos

  • Suponga la siguiente situación: tiene un objeto que puede almacenar cualquier objeto basado en una clave (básicamente, IDictionary). Desea almacenar objetos de varios tipos en él que no están directamente relacionados. (Por ejemplo, el diccionario puede ser un ASP.NET Sessiono puede representar un diccionario que se serializará en el disco para almacenamiento persistente).

    No quiero crear una sola clase que contenga todos esos objetos, porque no están directamente relacionados, vienen de diferentes lugares. Pero si almacena cada objeto por separado, significa que tiene que usar conversiones cuando obtiene algún valor y no hay verificación de tipo cuando lo configura.

    Para resolver esto, creé un tipo genérico que encapsula la clave junto con el tipo asociado y un par de métodos de extensión que lo usan:

    class TypedKey
    {
    public string Name { get; private set; }

    public TypedKey(string name)
    {
    Name = name;
    }
    }

    static class DictionaryExtensions
    {
    public static T Get(this IDictionary dictionary, TypedKey key)
    {
    return (T)dictionary[key.Name];
    }

    public static void Set(this IDictionary dictionary, TypedKey key, T value)
    {
    dictionary[key.Name] = value;
    }
    }

    Uso:

    private static readonly TypedKey AgeKey = new TypedKey("age");



    dictionary.Get(AgeKey) > 18
    dictionary.Set(AgeKey, age)

    Esto tiene la seguridad de tipo (tanto en get como en set) de usar una propiedad, mientras está respaldado por un diccionario que puede almacenar cualquier cosa.

    ¿Qué opinas de este patrón?

    ¡Hermosa solución!

    Usaría Convert.ChangeType en lugar de convertir ingenuamente a T. ¡Deje que el marco TypeDescriptor maneje las conversiones de objetos por usted!

  • Su sugerencia no es realmente segura, ya que aún puede pasar una clave del tipo incorrecto. Por lo tanto, solo usaría una clave normal (cadena). Pero agregaría un TryGetmétodo genérico que tiene en cuenta el tipo. El colocador no necesita ser genérico.

    static class DictionaryExtensions
    {
    public static T Get(this IDictionary dictionary, string key)
    {
    return (T)dictionary[key];
    }

    public static bool TryGet(this IDictionary dictionary,
    string key, out T value)
    {
    object result;
    if (dictionary.TryGetValue(key, out result) && result is T) {
    value = (T)result;
    return true;
    }
    value = default(T);
    return false;
    }

    public static void Set(this IDictionary dictionary,
    string key, object value)
    {
    dictionary[key] = value;
    }
    }

    A continuación, puede utilizar el diccionario de esta manera.

    int age = 20;
    dictionary.Set("age", age);

    // ...

    age = dictionary.Get("age");

    // or the safe way
    if (dictionary.TryGet("age", out age)) {
    Console.WriteLine("The age is {0}", age);
    } else {
    Console.WriteLine("Age not found or of wrong type");
    }

    Tenga en cuenta que el compilador puede inferir el tipo genérico al usar TryGet.


    ACTUALIZAR

    A pesar de mi sugerencia anterior, debo estar de acuerdo en que su solución es elegante. Aquí hay otra sugerencia que se basa en su solución, pero que encapsula el diccionario en lugar de proporcionar una clave. Bueno, actúa como envoltorio y como clave al mismo tiempo.

    public class Property
    {
    Dictionary _dict;

    public Property (Dictionary dict)
    {
    _dict = dict;
    }

    public T Value {
    get { return (T)_dict[this]; }
    set { _dict[this] = value; }
    }
    }

    Alternativamente, se podría proporcionar una clave de cadena en el constructor de la propiedad.

    Puedes usarlo así

    private static readonly Dictionary _properties = 
    new Dictionary();
    private static readonly Property _age = new Property(_properties);

    ...

    _age.Value > 18
    _age.Value = age

    El problema con esto es que tienes que especificar el tipo cada vez que usas `Get ()` y que es posible usar el tipo incorrecto con `Set ()`, lo que provocará una excepción de tiempo de ejecución en un `Get () posterior ". Pero tienes razón en que mi solución no es completamente segura para los tipos.

    He vuelto a retomar tu idea original y la he desarrollado un poco más. Por favor vea mi actualización.

    Ciertamente es una idea interesante. Sí, creo que es mejor de lo que propuse originalmente. Aunque usar el objeto en sí como clave (con igualdad de referencia) no funcionaría en mis casos, porque el `Diccionario` persiste. Pero como mencionaste, eso se puede modificar fácilmente.

Licencia bajo CC-BY-SA con atribución


Contenido fechado antes 24/07/2021 11:53

Etiquetas utilizadas