Metodo statico nell'interfaccia

1

Sto lavorando al software per riprodurre i file di registro. Disponiamo di diversi tipi di log da diversi provider.

Ho creato un'interfaccia che diverse classi implementeranno per riprodurre registri diversi. Ad esempio, ClassA passerà "somelog.txt" dal ProviderA, ClassB gestirà "another.log" dal ProviderB, ecc.

public interface ILogServer
{
    bool IsThisYourFile(string fileName, string[] content);
    // Other stuff not relevant to the question....
}

Uso una fabbrica per creare l'oggetto. Per ogni classe, la factory chiama IsThisYourFile() che valuta il nome del file e / o il contenuto per determinare se il file è del tipo che gestisce.

Idealmente creerei IsThisYourFile static ma non è permesso. Voglio usare reflection per ottenere tutte le classi che implementano ILogServer e chiama questa funzione.

var types = from type in Assembly.GetExecutingAssembly().GetTypes()
                where type.GetInterface("ILogServer") != null
                select type;

foreach (var t in types)
{
    // Call the static function
    if ((bool)t.GetMethod("IsThisYourFile").Invoke(null, new object[] { fileName, content }))
    {
        server = (ILogServer)Activator.CreateInstance(t);
        break;
    }
}

In questo modo, creo solo un oggetto, invece di creare un oggetto di ogni classe finché non viene trovato quello giusto, lasciando gli altri al GC.

La mia soluzione attuale è rimuovere IsThisYourFile dall'interfaccia e ricordati di implementarla in ogni classe come una funzione static . Ma non sembra giusto.

Mi chiedo come gli altri potrebbero implementarlo.

    
posta Johnny Mopp 06.02.2017 - 15:42
fonte

3 risposte

3

Mi sembra che tu stia tentando di replicare le meccaniche già messe a tua disposizione dal "semplice vecchio" orientamento all'oggetto, motivato da un'ipotesi piuttosto malriposta:

This way, I only actually create one object, instead of creating an object of every class until the right one is found, leaving the others to the GC.

Se ti preoccupi delle prestazioni o dell'impronta di memoria che istanzia tutte le implementazioni ILogServer , allora non farlo. Reflection è molto più costoso di un semplice ciclo in un elenco di implementazioni, e l'effettiva impronta di memoria di avere la lista è sicuramente trascurabile, dal momento che sono solo implementazioni di diverse strategie su come leggere i file di log, che non lasciano molto spazio per il trasporto di stati pesanti.

TL; DR: invece di usare reflection per trovare tutte le implementazioni della tua interfaccia, puoi semplicemente istanziare tutte le possibili implementazioni di ILogServer e tenerle in una lista, da cui puoi chiamare IsThisYourFile finché non trovi una corrispondenza . Semplice vecchio OO, nessun complesso strumento coinvolto .

    
risposta data 06.02.2017 - 17:01
fonte
1

Avere una classe specializzata per testare i file casuali per determinare se hai una corrispondenza non mi sembra così elegante o efficace. Preferirei creare una classe separata, che potrebbe essere statica, che conosca i diversi tipi di file supportati, rilevare il tipo per ogni dato file. Quindi puoi creare immediatamente la classe del lettore corrispondente, non dovrai indovinare.

La classe del rivelatore potrebbe restituire un enum che potrebbe essere utilizzato in uno switch per istanziare la classe appropriata, passando il percorso del file al costruttore. L'enumerazione includerebbe un membro NotRecognized. Usare la riflessione mi sembra inutile per le tue esigenze.

    
risposta data 06.02.2017 - 16:12
fonte
1

Chiama il membro dell'interfaccia senza un'istanza

Puoi includere IsThisYourFile nell'interfaccia e chiamarla senza istanziare un'istanza della classe logger.

Trucco poco noto: usa un "delegato membro di un'istanza aperta", passando null come istanza:

bool CanClassHandleFile(Type type, string path)
{
    if (type.GetInterface("ILogServer") == null) return false;

    //Get the info for the method you want to call
    var method = type.GetMethod("IsThisYourFile");

    //Create open instance member delegate
    var func = (Func<type,string,bool>)Delegate.CreateDelegate(typeof(Func<type, string, bool>), method);

    //Invoke the delegate without an instance
    return func(null, path);          
}

Questo è piuttosto oscuro (non posso dire di raccomandarlo onestamente) ma fa esattamente quello che vuoi. Otterrai un errore in fase di compilazione se non includi IsThisYourFile nell'implementazione e il tuo programma sarà in grado di scorrere i tipi di ILogServer e controllare ciascuno per vedere se supporta il file, senza dover istanziarli.

Attenzione: se IsThisYourFile tenta di leggere qualsiasi variabile membro non statica, genererà un'eccezione di riferimento null. Se prova a leggere this , restituirà null . Normalmente non si rileva questo problema fino al momento dell'esecuzione, ma è possibile attenuare il rischio seguendo un modello in cui il membro dell'interfaccia passa immediatamente il controllo a un membro statico, ad es.

class LogServerOne : ILogServer
{
    static private bool CheckFile(string path) //Notice this is private.  Name doesn't matter.
    {
        //Do the actual logic
    }

    public bool IsThisYourFile(string path)
    {
        return LogServerOne.CheckFile(path); //Should always be only one line of code here
    }
}
    
risposta data 06.02.2017 - 23:39
fonte

Leggi altre domande sui tag