2016-06-27 12 views
6

Angesichts der folgenden Code verwendet (was ich weiß nicht kompiliert und ist nicht korrekt):abgeleiteten Typ als Muttertyp umgewandelt Allow `object` zurückzukehren, aber die abgeleiteten Typ Methode

public abstract class Fetcher { 
    public abstract IEnumerable<object> Fetch(); 
} 

public class Fetcher<T> : Fetcher { 
    private readonly Func<IEnumerable<T>> _fetcher; 
    public Fetcher(Func<IEnumerable<T>> fetcher) { _fetcher = fetcher; } 
    public IEnumerable<T> Fetch() => _fetcher(); // override or new 
} 

Und das Beispiel-Setup:

var myFetchers = new List<Fetcher> { 
    new Fetcher<string>(() => new List<string> { "white", "black" })), 
    new Fetcher<int>(() => new List<int> { 1, 2 })) 
}; 

Wie kann ich meinen Code so strukturieren, dass dies funktioniert?

IEnumerable<IEnumerable<object>> fetcherResults = 
    myFetchers.Select(fetcher => fetcher.Fetch()); // get objects from derived method 

TLDR; Ich habe darüber ein wenig mehr nachgedacht. Eine andere Möglichkeit, meine Frage zu beantworten, ist: Wie führe ich die Methode Fetch für alle Elemente in der Liste aus und speichere eine Sammlung ihrer Ergebnisse, ohne den Basistyp jedes Elements bestimmen und eine Umwandlung in diesen Typ durchführen zu müssen. während nicht IEnumerable<object> von der Fetch Methode selbst zurückkehrt?

Der Schlüssel hier ist, dass zu der Zeit die Objekte unterschiedlicher generischer Art in einer Auflistung des übergeordneten Typs sind, möchte ich die Methode des abgeleiteten Typs ausführen, aber das Ergebnis als IEnumerable<object>, nicht IEnumerable<T> zurückgeben. (Dies kann bei Bedarf mit einer anderen Methode geschehen, nehme ich an, obwohl es nett wäre, wenn es denselben Namen hätte.) Dies ist so, dass ein Abschnitt des Codes, der nichts über Abrufer wissen muss, seine Daten haben kann (und übergeben werden,), aber der Teil des Codes, der Fetchers kümmert, kann vollständig ignorieren, welche Art von Daten die Fetchers arbeiten (nicht seine Sorge, nur der Verbraucher der Daten muss mit den spezifischen Typen arbeiten).

Ich muss hier nicht Vererbung verwenden, und die Schnittstelle IFetcher würde genauso gut funktionieren. Ich denke immer darüber, wie explizite Schnittstellenimplementierung helfen könnte, aber ich bin zu schließen gerade die Schleife nicht gerade jetzt, wie diese Arbeit für mich zu machen:

// in a class implementing ICollection<T> 
public IEnumerator<T> GetEnumerator() => _collection.GetEnumerator(); 
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) _collection).GetEnumerator(); 

Wie üblich, die reale Situation ist komplizierter, als dies (wie wie die Daten, die die Abrufer liefern, die aus einer Datenbank stammen, usw.). Aber diesen einen Teil runter zu bekommen, wird alles sein was ich brauche.

Ich habe die folgenden Ressourcen vergeblich angeschaut: implement an inheritable method that returns an object, call method on generic type from abstract parent.

+0

Warum nicht haben IEnumerable FetchGeneric() 'und' IEnumerable Fetch() ', die einfach' FetchGeneric() aufgerufen. Cast () '? –

+0

Ich glaube, ich wurde abgeworfen, weil ich wollte, dass sie den gleichen Namen hatten. Es scheint jetzt sehr offensichtlich, dass Sie das erwähnen. Aber wenn ich mich dazu entschließen würde, das Abrufen zu verschieben, bis später, wenn ich einen abgeleiteten Typ verwende, wäre es sicher schön, den spezifischen Typ unter Verwendung desselben Methodennamens zu erhalten. – ErikE

+0

Ich habe meine Frage aktualisiert, siehe den neuen Absatz, der TLDR startet; – ErikE

Antwort

3

Es gibt einen direkten Weg, dies mit expliziter Implementierung zu tun, wie Sie vermuteten. Dies ist nur verfügbar, wenn Sie eine Schnittstelle anstelle einer abstrakten Klasse verwenden.

public interface IFetcher { 
    IEnumerable<object> Fetch(); 
} 

public class Fetcher<T> : IFetcher { 
    private readonly Func<IEnumerable<T>> _fetcher; 
    public Fetcher(Func<IEnumerable<T>> fetcher) { _fetcher = fetcher; } 
    IEnumerable<object> IFetcher.Fetch() => Fetch().Cast<object>(); 
    public IEnumerable<T> Fetch() => _fetcher(); 
} 

In dieser Implementierung die Schnittstelle Version des Anrufs kehrt IEnumerable<object>, während die Klasse zugängliche Version gibt die spezifischeren IEnumerable<T>. Wenn Sie den Abholer über die Schnittstelle aufrufen, wird er zur ersten aufgelöst. Der Aufruf durch die Klasse wird zur zweiten aufgelöst.

+0

Ja! Das ist, was ich gesucht habe. Warum konnte ich es nicht sehen? Vielen Dank! – ErikE

2

Wenn ich die Frage richtig verstanden habe, dann können Sie das Problem lösen, indem Fetcher<T> implementieren eine generische Schnittstelle IFetcher<T> wie diese machen:

public interface IFetcher<T> 
{ 
    IEnumerable<T> Fetch(); 
} 

public class Fetcher<T> : IFetcher<T> 
{ 
    private readonly Func<IEnumerable<T>> _fetcher; 
    public Fetcher(Func<IEnumerable<T>> fetcher) { _fetcher = fetcher; } 
    public IEnumerable<T> Fetch() => _fetcher(); 
} 

Und dann einen Adapter zu schaffen, IFetcher<TFrom> zu einem IFetcher<TTo> wie folgt umwandeln kann:

public class Adaptor<TFrom, TTo> : IFetcher<TTo> 
{ 
    private readonly IFetcher<TFrom> _fetcher; 

    public Adaptor(IFetcher<TFrom> fetcher) 
    { 
     _fetcher = fetcher; 
    } 

    public IEnumerable<TTo> Fetch() 
    { 
     return _fetcher.Fetch().Cast<TTo>(); 
    } 
} 

Dies ermöglicht zum Beispiel IFetcher<string>-IFetcher<object> zu konvertieren.

Mit Hilfe dieser Erweiterung Methode:

public static class ExtensionMethods 
{ 
    public static Converter<TFrom> Convert<TFrom>(this IFetcher<TFrom> fetcher) 
    { 
     return new Converter<TFrom>(fetcher); 
    } 

    public class Converter<TFrom> 
    { 
     private readonly IFetcher<TFrom> _fetcher; 

     public Converter(IFetcher<TFrom> fetcher) 
     { 
      _fetcher = fetcher; 
     } 

     public IFetcher<TTo> To<TTo>() 
     { 
      return new Adaptor<TFrom, TTo>(_fetcher); 
     } 
    } 
} 

Sie können ganz einfach etwas tun:

static void Main(string[] args) 
{ 
    var myFetchers = new List<IFetcher<object>> 
    { 
     new Fetcher<string>(() => new List<string> { "white", "black" }).Convert().To<object>(), 
     new Fetcher<int>(() => new List<int> { 1, 2 }).Convert().To<object>() 
    }; 

    var results = myFetchers.Select(x => x.Fetch()); 
} 

Bitte beachten Sie, dass Sie ohne die Erweiterung Methode zu tun, aber es wird Ihre Code lesbarer.

+0

Ich verstehe es. Sie sagen, Fetcher später mit einer anderen Indirektion zu umbrechen, die einen zugrundeliegenden Fetcher verwendet und dann die objekt-zurückkehrende Schnittstelle implementiert, sodass wir später beim Abrufen die richtige Methode ausführen, die aber in den gewünschten Objekttyp konvertiert wird. Sie lassen den Compiler den Typ ableiten und speichern dieses Wissen dann in einem Wrapper. Ich muss darüber nachdenken, es ist sicher nützlich, aber nicht sicher, ob es die Route ist, die ich hinuntergehen möchte. – ErikE

Verwandte Themen