2012-01-09 14 views
6

Ho un dizionario generico di oggetti in cui la chiave è di tipo Type:impostazione del tipo di sicurezza di oggetti con un dizionario che ha un `type` chiave

public class DynamicObject : IDictionary<Type, object> 

L'idea è che questo oggetto è condiviso in un'architettura basata su plugin e quindi un tipo (che potrebbe risiedere in un plugin .dll) viene utilizzato come chiave per evitare conflitti tra i plugin. Questo tipo viene anche utilizzato per memorizzare i metadati relativi al campo (ad esempio una descrizione di quel campo o il tipo effettivo di quel campo).

Attualmente i plugin necessario per impostare il valore di campi su questo oggetto utilizzando il codice simile al seguente:

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

L'unico problema è che questo non è di tipo sicuro - anche se il tipo UsernameField è consapevole del tipo esatto del valore che si aspetta (ad esempio int o in questo caso string), il valore fornito qui viene semplicemente digitato come oggetto. Mi piacerebbe usare i generici per rendere sicura l'impostazione/acquisizione del tipo di proprietà, ma non sono sicuro di come. Finora il migliore che è venuta in mente è questa:

// 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); 

Questo è sicuro tipo, tuttavia significa che ciascuno dei miei tipi di campo (ad esempio UsernameField) ha un metodo statico quasi identico Set.

Come posso accedere in modo sicuro ai valori in questo modo senza avere molti metodi quasi identici su ciascuno dei miei tipi di campo?

Per inciso, sta usando Type come una chiave come questa una buona idea o ci sono insidie ​​nascoste di cui non sono ancora a conoscenza?

+0

qual è la relazione tra il 'type' e il' object' qui? l'oggetto 'è sempre di tipo' Type'? –

+0

Puoi dare nomi più significativi a MyObject e MyField in modo che possiamo capire il loro significato? –

+0

@MarcGravell Al momento i valori non sono effettivamente dello stesso tipo della chiave, quindi in 'someValue' è in realtà una' stringa' non un 'MyField'. Detto questo, potrei cambiarlo in modo che questo sia il caso (avendo visto la tua risposta ora cancellata) – Justin

risposta

2

definire un'interfaccia che tutti i plugin devono implementare:

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

e quindi utilizzare un Dictionary<Type, IPlugIn>.

In genere l'interfaccia è dichiarata in una dll separata (come "MyCompany.MyProject.PlugIns.Contracts.dll").


EDIT: Ok, penso di sapere cosa intendi ora.

Il trucco è di avere una classe generica tra Field e UsernameField con un metodo generico Set. Il fatto che Field non sia generico, rende tutti i tipi di campo assegnabili ad esso. Questo non sarebbe il caso, se fosse stato dichiarato come 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 

} 

Perché abbiamo bisogno di chiamare GetType nel metodo Set, non possiamo dichiararli come static. Pertanto, ho usato il modello singleton.

Ora, siamo in grado di impostare il campo in un tipo modo sicuro:

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

OLTRE 1:

Da oggi i campi sono single, è possibile utilizzare i campi come chiave della dizionario invece del loro tipo.

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

E Set sarebbe diventato:

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

OLTRE 2:

Si potrebbe anche definire DynamicObject in questo modo:

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

Ora è possibile impostare i valori lik e questo:

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

Questo è sicuro e sembra più naturale. Il metodo Set in GenericField è obsoleto ora.

+0

Questa è una possibilità (anche se i campi potrebbero essere condivisi tra plugin e quindi userei 'interfaccia IField' e' dizionario 'invece), tuttavia non sembra che" pulito "... – Justin

+0

Come alternativa all'utilizzo di 'GetType' in questa situazione è possibile utilizzare' MethodInfo.GetCurrentMethod(). ReflectedType' se la preferenza è rendere 'Set' static. –

+0

Ciò restituire 'DynamicFields.GenericField \' 1 [T] ', ma non la classe derivata. –

1

Potrei semplicemente mancare il punto della domanda ma se l'obiettivo è ridurre la quantità di duplicazione del codice richiesta per raggiungere la sicurezza del tipo, allora perché non definire gli helper di Accessor in base al tipo di campo piuttosto che al campo stesso:

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); 

Edit:

Oppure si potrebbe usare una leggera variazione del Oliver's solution senza 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 non dovrebbe essere a conoscenza di un DynamicObject per ottenere e impostare il valore. Si potrebbe fare in modo che DyanmicObject gestisca l'acquisizione e l'impostazione dei valori, ma penso che un approccio migliore sia quello di trattare lo DynamicObject come una raccolta di istanze Field e ogni campo ha il proprio valore. Qualcosa di simile a questo:

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(); 
    } 
} 

E l'uso sarebbe allora:

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); 
} 
Problemi correlati