2012-01-09 9 views
6

tengo un diccionario genérico de objetos, donde la clave es de tipo Type:configuración de seguridad de tipos de objetos con un diccionario que tiene un `type` clave

public class DynamicObject : IDictionary<Type, object> 

La idea es que este objeto está compartida en una arquitectura basada en complementos y, por lo tanto, un tipo (que podría residir en un plugin .dll) se utiliza como clave para evitar conflictos entre complementos. Este tipo también se usa para almacenar metadatos sobre el campo (como una descripción de ese campo o el tipo real de ese campo).

plugins

Actualmente necesitan para establecer el valor de los campos en este objeto mediante código similar al siguiente:

DynamicObject someObject; 
string someValue; 
someObject[typeof(UsernameField)] = someValue; 

El único problema con esto es que esto no es de tipo seguro - a pesar de que el tipo UsernameField es consciente del tipo exacto del valor que está esperando (p. ej., int o en este caso string), el valor proporcionado aquí solo se escribe como un objeto. Me gustaría utilizar genéricos para hacer que la configuración/obtención de propiedades sea segura, pero no estoy seguro de cómo. Hasta el momento los mejores que he llegado con esto es:

// Field is a base class for all types used as keys on DynamicObject 
[Description("Username of the user")] 
public class UsernameField : Field 
{ 
    public static void Set(DynamicObject obj, string value) 
    { 
     obj[typeof(UsernameField)] = someValue; 
    } 
} 

// To set fields 
UsernameField.Set(obj, someValue); 

Esto es seguro tipo, sin embargo, significa que cada uno de mis tipos de campo (por ejemplo UsernameField) tiene un casi idéntico método estático Set.

¿Cómo puedo tener acceso de tipo seguro a los valores de esta manera sin tener muchos métodos casi idénticos en cada uno de mis tipos de campo?

Como un lado, está utilizando Type como una buena idea como esta clave o hay trampas ocultas que aún no conozco?

+0

cuál es la relación entre el '' type' y la object' aquí? ¿El 'object' siempre es de tipo' Type'? –

+0

¿Puedes dar nombres más significativos a MyObject y MyField para que podamos entender su significado? –

+0

@MarcGravell Por el momento, los valores no son en realidad del mismo tipo que la clave, por lo que en el anterior 'someValue' es en realidad una 'cadena' no un 'MyField'. Habiendo dicho eso, podría cambiarlo para que este sea el caso (habiendo visto tu respuesta ahora eliminada) – Justin

Respuesta

2

definir una interfaz que todos los plugins deben implementar:

public interface IPlugin { 
    string Name { get; } 
    string Author { get; } 
    string Description { get; } 
    void Init(); 
} 

y luego usar un Dictionary<Type, IPlugIn>.

Normalmente, la interfaz se declara en un dll diferente (como "MyCompany.MyProject.PlugIns.Contracts.dll").


EDIT: Ok, creo que sé lo que quieres decir ahora.

El truco es tener una clase genérica entre Field y UsernameField con un método genérico Set. El hecho de que Field no sea genérico hace que todos los tipos de campo sean asignables. Este no sería el caso, si se declaró como Field<T>.

public abstract class Field 
{ 
} 

public abstract class GenericField<T> : Field 
{ 
    public void Set(DynamicObject obj, T value) 
    { 
     obj[this.GetType()] = value; 
    } 
} 

public class UsernameField : GenericField<string> 
{ 
    #region Singleton Pattern 

    public static readonly UsernameField Instance = new UsernameField(); 

    private UsernameField() { } 

    #endregion 

} 

Porque tenemos que llamar GetType en el método Set, no podemos declarar como static. Por lo tanto, utilicé el patrón singleton.

Ahora, podemos establecer el campo de una manera segura Tipo:

UsernameField.Instance.Set(obj, "Joe"); 

ADEMÁS 1:

Desde ahora los campos son únicos, se puede usar los campos como clave de la diccionario en lugar de su tipo.

public class DynamicObject : IDictionary<Field, object> { } 

Y Set se convertiría en:

public void Set(DynamicObject obj, T value) 
{ 
    obj[this] = value; 
} 

ADEMÁS 2:

También podría definir DynamicObject así:

public class DynamicObject : Dictionary<Field, object> 
{ 
    public void Set<T>(GenericField<T> field, T value) 
    { 
     this[field] = value; 
    } 
} 

Ahora puede establecer valores lik e this:

obj.Set(UsernameField.Instance, "Sue"); 

Esto es tipo seguro y parece más natural. El método Set en GenericField ahora está obsoleto.

+0

Esta es una posibilidad (aunque los campos podrían ser compartidos entre complementos y así usaría 'interface IField' y' Dictionary 'en su lugar), sin embargo no parece tan" limpio "... – Justin

+0

Como En lugar de usar 'GetType' en esta situación, puede usar' MethodInfo.GetCurrentMethod(). ReflectedType' si la preferencia es hacer que 'Set' sea estático. –

+0

Esto devolvería 'DynamicFields.GenericField \' 1 [T] ', pero no la clase derivada. –

1

Es posible que me pierda el objetivo de la pregunta, pero si el objetivo es reducir la cantidad de duplicación de código necesaria para lograr seguridad tipo, ¿por qué no definir los descriptores de acceso según el tipo de campo en lugar del campo en sí?

public abstract class StringField<T> : Field 
{ 
    public static void Set(DynamicObject obj, string value) 
    { 
     obj[typeof(T)] = someValue; 
    } 
} 

public class UsernameField : StringField<UsernameField> { } 

// To set fields 
UsernameField.Set(obj, someValue); 

Editar:

O usted podría utilizar una ligera variación del Oliver's solution sin el Singleton:

public abstract class GenericField<T, U> : Field 
{ 
    public static void Set(DynamicObject obj, T value) 
    { 
     obj[typeof(U)] = value; 
    } 
} 

public class UsernameField : GenericField<string, UsernameField> { } 
1

Fields no debería tener que saber acerca de un DynamicObject para obtener y establecer el valor. Puede hacer que DyanmicObject maneje la obtención y configuración de valores, pero creo que un mejor enfoque es tratar el DynamicObject como una colección de instancias Field, y cada campo tiene su propio valor. Algo como esto:

interface IField 
{ 
    object Value { get; set; } 
} 

interface IField<T> : IField 
{ 
    new T Value { get; set; } 
} 

abstract class BaseField<T> : IField<T> 
{ 
    T _value; 
    public T Value 
    { 
     get { return _value; } 
     set 
     { 
      // could add stuff like OnValueChanging, IsValueValid, etc... 
      this._value = value; 
     } 
    } 

    object IField.Value 
    { 
     get { return Value; } 
     set { Value = (T)value; } 
    } 
} 

class DynamicObject : List<IField> 
{ 
    public TField GetField<TField>() where TField : IField 
    { 
     return this.OfType<TField>().Single(); 
    } 
} 

Y el uso sería por tanto:

class UsernameField : BaseField<string> { } 

[TestMethod] 
public void test() 
{ 
    var parent = new DynamicObject(); 
    var field = new UsernameField() { Value = "the username" }; 
    parent.Add(field); 
    Assert.AreEqual("the username", parent.GetField<UsernameField>().Value); 
} 
Cuestiones relacionadas