2009-02-20 14 views

risposta

25

Questo è quello che ho finito per andare con, non ho visto il valore in Aggiunta di un attributo di classe di tenere un chiave di risorsa e poi cercare nei file di risorse - perché non usare solo il nome enumerazione + valore come una chiave di risorsa?

using System; 
using System.Resources; 
using System.Reflection; 

public class MyClass 
{ 
    enum SomeEnum {Small,Large}; 

    private ResourceManager _resources = new ResourceManager("MyClass.myResources", 
          System.Reflection.Assembly.GetExecutingAssembly());  

    public string EnumDescription(Enum enumerator) 
    {  
    string rk = String.Format("{0}.{1}",enumerator.GetType(),enumerator); 
    string localizedDescription = _resources.GetString(rk); 

    if (localizedDescription == null) 
     { 
     // A localized string was not found so you can either just return 
     // the enums value - most likely readable and a good fallback. 
     return enumerator.ToString(); 

     // Or you can return the full resourceKey which will be helpful when 
     // editing the resource files(e.g. MyClass+SomeEnum.Small) 
     // return resourceKey; 
     } 
    else 
     return localizedDescription; 
    } 


    void SomeRoutine() 
    { 
    // Looks in resource file for a string matching the key 
    // "MyClass+SomeEnum.Large" 
    string s1 = EnumDescription(SomeEnum.Large);  
    } 
} 
+4

un Enum non è un enumeratore, vero? È un tipo enumerato, ma un enumeratore è qualcosa di molto diverso, credo ... – Svish

+4

Usando C# 3.5, puoi fare di quel metodo un metodo di estensione in modo che tu possa usare SomeEnum.Large.EnumDescription(); –

+2

Si è appena imbattuto in questa domanda durante la ricerca di un altro problema. Vorrei solo ricordare che l'uso del tipo e dei nomi dei membri rende l'applicazione più difficile da offuscare (è necessario escludere la dichiarazione sensibile dal processo). –

8

c'è una soluzione semplice: utilizza l'attributo LocalizedDescription per passare una chiave di risorsa.

[Serializable] 
    public class LocalizableDescriptionAttribute:DescriptionAttribute 
    { 
     public LocalizableDescriptionAttribute(string resourceKey) 
      :base(Resources.ResourceManager.GetString(resourceKey)) 
     { } 

    } 
+0

io non sono sicuro di quello che questo si ottiene oltre utilizzando le enumerazioni nome di tipo completo (namespace.enumname.value) come ResourceKey.Sembra un passaggio superfluo (usando reflection per ottenere LocalizableDescriptionAttribute) semplicemente andando al resourceManager - cosa mi manca? – Ryan

+0

Penso che abbiamo un malinteso. Uso il mio attributo per localizzare la descrizione dei valori enum. Immagino tu stia parlando di localizzare le enumerazioni ma non i loro valori. – Valentin

+0

Non è possibile decorare il valore enum con l'attributo Descrizione e passare una chiave di risorsa. – Valentin

0

Vedi il mio esempio tabella in questa domanda: mappe tipo

Localisation/I18n of database data in LINQ to SQL

Lo stato di tabella per i valori di enumerazione. Il vero vantaggio qui è che puoi avere localizzazione nei tuoi report e attraverso le tue applicazioni e specificare ID esterni per l'integrazione con terze parti che non vogliono i tuoi valori interni, ecc. Scollega la descrizione dell'enum dal suo valore.

+0

Bel metodo per le applicazioni db centric, ma davvero eccessivo per le mie esigenze. – Ryan

0

Non è possibile applicare più System.ComponentModel.DescriptionAttribute (in modo che l'opzione non sia disponibile).

Quindi aggiungere un livello di riferimento indiretto, la descrizione contiene un nome di risorsa e quindi utilizzare il supporto di localizzazione nelle risorse. Chiaramente gli utenti dell'enum dovranno chiamare il metodo di supporto per farlo.

+0

Questo è ciò che Valentin Vasiliev propone, ma senza la necessità di utilizzare un metodo di supporto - vale lo stesso commento. – Ryan

3

Un modo in cui l'ho fatto una volta, consisteva nell'aggiungere un metodo di estensione nello stesso spazio dei nomi di un enum, che restituiva una stringa. Nel mio caso era solo hardcoded, ma non sarebbe stato un problema ottenerli da un file di risorse.

public static string Describe(this SomeEnum e) 
    { 
     switch(e) 
     { 
      SomeEnum.A: 
       return "Some text from resourcefile"; 
      SomeEnum.B: 
       return "Some other text from resourcefile"; 
      ...: 
       return ...; 
     } 
    } 
non

Forse una soluzione estremamente liscia o fantasia, ma funziona =)

+0

+ 1 per l'utilizzo di un metodo di estensione ... anche se preferirei usare il nome completo del tipo di enum come chiave di risorsa (vedi risposta di Ryans) –

+0

@ SDX2000: Sì, quello potrebbe essere un modo molto bello alternativo per ottenere le stringhe . – Svish

23

La mia soluzione, mediante l'attributo decription origini: dichiarazione

public class LocalizedEnumAttribute : DescriptionAttribute 
{ 
    private PropertyInfo _nameProperty; 
    private Type _resourceType; 

    public LocalizedEnumAttribute(string displayNameKey) 
     : base(displayNameKey) 
    { 

    } 

    public Type NameResourceType 
    { 
     get 
     { 
      return _resourceType; 
     } 
     set 
     { 
      _resourceType = value; 

      _nameProperty = _resourceType.GetProperty(this.Description, BindingFlags.Static | BindingFlags.Public); 
     } 
    } 

    public override string Description 
    { 
     get 
     { 
      //check if nameProperty is null and return original display name value 
      if (_nameProperty == null) 
      { 
       return base.Description; 
      } 

      return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null); 
     } 
    } 
} 

public static class EnumExtender 
{ 
    public static string GetLocalizedDescription(this Enum @enum) 
    { 
     if (@enum == null) 
      return null; 

     string description = @enum.ToString(); 

     FieldInfo fieldInfo = @enum.GetType().GetField(description); 
     DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); 

     if (attributes.Any()) 
      return attributes[0].Description; 

     return description; 
    } 
} 

L'Enum

public enum MyEnum 
{ 
    [LocalizedEnum("ResourceName", NameResourceType = typeof(ResourceType))] 
    Test = 0 
} 

quindi chiamare MyEnumInstance.GetLocalizedDescription()

+0

Needs BindingFlags.NonPublic e deve accedere a base.Description all'interno di NameResourceType.set. Vedi la mia risposta per il supporto di enum flags. – kerem

+0

Questo mi sembra molto più riutilizzabile rispetto all'altra soluzione. Grazie – Peter

+0

Ho dovuto cambiare 'NameResourceType' in' typeof (Risorsa) 'per farlo funzionare. Grazie! – pizycki

1

Sostituisci il metodo di @ nairik con quanto segue per aggiungere supporto per enumerazioni di flag.

public static string GetLocalizedDescription(this Enum @enum) 
{ 
    if (@enum == null) 
     return null; 

    StringBuilder sbRet = new StringBuilder(); 

    string description = @enum.ToString(); 

    var fields = description.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); 

    foreach (var field in fields) 
    { 
     FieldInfo fieldInfo = @enum.GetType().GetField(field); 
     DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false); 

     if (attributes.Any()) 
      sbRet.AppendFormat("{0}, ", attributes[0].Description); 
     else 
      sbRet.AppendFormat("{0}, ", field); 
    } 

    if (sbRet.Length > 2) 
     sbRet.Remove(sbRet.Length - 2, 2); 

    return sbRet.ToString(); 
} 

e sostituire NameResourceType nell'attributo:

public Type NameResourceType 
{ 
    get 
    { 
     return _resourceType; 
    } 
    set 
    { 
     _resourceType = value; 

     _nameProperty = _resourceType.GetProperty(base.Description, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); 
    } 
} 
Problemi correlati