Domanda

Sto creando un client di chat di rete in C # come progetto secondario. Oltre ai semplici messaggi di testo, ho anche comandi con prefisso che possono essere inseriti nell'Input TextBox. Ho usato un approccio modulare creando un enum che contiene tutti i vari comandi e quindi decorando quei comandi con attributi.

Gli attributi specificano quale comando con prefisso può essere immesso per attivare il comando, nonché eventuali alias all'identificatore del comando principale e all'utilizzo del comando.

Esempio:

public enum CommandType : byte
{
    [PrimaryIdentifier("file"),
     AdditionalIdentifier("f"),
     CommandUsage("[<recipient>] [<filelocation>]")]
    FileTransferInitiation,

    [PrimaryIdentifier("accept"),
     AdditionalIdentifier("a")]
    AcceptFileTransfer,

    // ...
}

Il mio problema sorge quando provo a consentire più alias al comando principale. Ho provato questo in due modi: autorizzando i duplicati dell'attributo AdditionalIdentifier o trasformando l'argomento del costruttore in AdditionalIdentifier una params string [] .

Con il primo, l'ho implementato decorando la classe di attributi con AttributeUsage e impostando AllowMultiple su true. Anche se questo effettivamente ottiene ciò che sto cercando, sento che potrebbe diventare molto rumoroso molto velocemente avere diverse linee di alias, oltre agli altri attributi.

Quest'ultimo funziona anche, tuttavia, genera avviso del compilatore CS3016 e afferma che tale approccio non è conforme a CLS. Ovviamente, questo non mi impedisce necessariamente di usarlo ancora, ma ho imparato a trattare sempre gli avvisi come errori.

La mia vera domanda è: dovrei ignorare le mie obiezioni con i duplicati e andare avanti e usarle, o c'è qualche altra soluzione che potrebbe essere usata?

Grazie.

È stato utile?

Soluzione

Personalmente andrei con l'approccio AllowMultiple: non credo che il "rumore" sarà un grosso problema a meno che tu non abbia davvero carichi di identificatori per ciascun comando. Ma se non ti piace e vuoi rimanere conforme a CLS, un'altra soluzione sarebbe quella di fornire costruttori sovraccaricati per AdditionalIdentifierAttribute:

public AdditionalIdentifierAttribute(string id) { ... }
public AdditionalIdentifierAttribute(string id1, string id2) { ... }
public AdditionalIdentifierAttribute(string id1, string id2, string id3) { ... }

Il rovescio della medaglia è che questo ti limita a un numero predeterminato di identificatori.

Detto questo, la conformità CLS è in realtà solo una considerazione importante se stai costruendo una libreria che altri potrebbero usare (e specificamente da altre lingue). Se questo tipo o la libreria è interna all'applicazione, è ragionevole ignorare gli avvisi di conformità CLS.

EDIT: riflettendo ulteriormente su questo, hai un sacco di attributi su quegli enumerazioni. Potresti prendere in considerazione la possibilità di creare una classe Command astratta e di esporre identificatori, utilizzo, ecc. Come proprietà di quella classe; quindi derivano tipi concreti di comando che restituiscono i valori appropriati da tali proprietà. Ciò potenzialmente consente anche di spostare la logica di gestione in tali oggetti Command anziché attivare il valore enum.

Altri suggerimenti

Puoi anche utilizzare " params string [] alias " nel costruttore per consentire un elenco di argomenti variabili:

[AttributeUsage(AttributeTargets.Method)]
class TestAttribute : Attribute
{
    public TestAttribute(params string[] aliases)
    {
        allowedAliases = aliases;
    }

    public string[] allowedAliases { get; set; }

}

Questo ti permetterebbe di fare:

[Test("test1", "test2", "test3")]
static void Main(string[] args)

Perché non avere un singolo attributo con più proprietà? Chiedi alla proprietà dell'alias di prendere un elenco separato da virgole. Questo è l'approccio che adottano in MVC per cose come AuthorizeAttribute for Roles. Internamente, la proprietà analizza la stringa in un array per facilitarne l'utilizzo nella classe di attributi, ma consente un modo semplice per impostare la configurazione.

public class IdentifierAttribute
{
    public string Name { get; set; }
    public string Usage { get; set; }

    private string[] aliasArray;
    private string aliases;
    public string Aliases
    {
         get { return this.aliases; }
         set
         {
             this.aliases = value;
             this.aliasArray = value.Split(',').Trim();
         }
    }
}

Quindi usalo come:

public enum CommandType : byte
{
     [Identifer( Name = "file", Aliases = "f", Usage = "..." )]
     FileTransferType,

     ...
}

Un altro approccio potrebbe essere quello di fare in modo che l'attributo prenda una matrice di stringhe come parametro del costruttore - in questo modo, si ottiene che il compilatore analizzi la matrice per te (a spese di un po 'più di goop quando si applica l'attributo) :

[Identifiers(new string[] {"Bill", "Ben", "Ted"})]

Un rapido esempio di implementazione di & amp; l'utilizzo di tale tecnica è simile al seguente:

using System;
using System.Collections.ObjectModel;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            SomeClass.TellMeAboutYourself();
        }
    }
    public class Identifiers : Attribute
    {
        private string[] names;
        public Identifiers(string[] someNames)
        {
            names = someNames;
        }
        public ReadOnlyCollection<string> Names { get { return new ReadOnlyCollection<string>(names); } }
    }
    [Identifiers(new string[] {"Bill", "Ben", "Ted"})]
    static class SomeClass
    {
        public static void TellMeAboutYourself()
        {
            Identifiers theAttribute = (Identifiers)Attribute.GetCustomAttribute(typeof(SomeClass), typeof(Identifiers));
            foreach (var s in theAttribute.Names)
            {
                Console.WriteLine(s);
            }
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top