War es hilfreich?

Lösung

Bearbeiten: Bitte beachten Sie, dass diese Antwort gegeben wurde, bevor die Frage in einer Bearbeitung vollständig geändert wurde. Aus diesem Grund bezieht es sich nun auf Dinge, die nur in der ursprünglich angegebenen Frage vorhanden waren. Ich bitte um Verzeihung um alle "baumelnden Zeiger". :-)


Kurze Antwort:

Mit dem Code, den Sie gepostet haben, sehe ich keine Alternative zum Casting zu IFoo<T>. Wenn Sie dies nicht tun, wird der Compiler eine Warnung geben (zumindest auf meiner Maschine).

Ausführlichere Antwort:

Muss Ihr Code tatsächlich so sein? Benötigen Sie die fragliche Besetzung in erster Linie?

Ich gehe davon aus, dass Sie Ihre Werksmethode mehr oder weniger so anrufen werden:

var stringFoo = FooFactory.CreateFoo<string>();

Sie müssen den Vorlageparameter angeben (string in diesem Fall) explizit, weil es aus keinem Methodenargument abgeleitet werden kann (in diesem Fall, weil es eigentlich überhaupt keine gibt). Offensichtlich wird die Fabrikmethode eine zurückgeben IFoo<string>.

Da Sie den Typ zur Laufzeit explizit angeben müssen, könnten Sie genauso gut schreiben:

var stringFoo = StringFoo.Create();

und haben daher eine Fabrikmethode im Inneren StringFoo, so wie dieses, das tut bedingungslos das Offensichtliche:

public class StringFoo : IFoo<string>
{
    ...

    public static StringFoo Create()  // or alternatively, return an IFoo<string>
    {
        return new StringFoo();
    }
}

Indem Sie dieses Muster auf andere anwenden IFoo<T> Auch implementierungen sparen Sie die if Kette oder switch Block im Inneren FooFactory.CreateFoo<T>, Machen Sie Ihren Code leichter und lassen Sie die Notwendigkeit los, die Besetzung zu besetzen (worüber Sie sich Sorgen machen).

Versteh mich nicht falsch, ich bin mir bewusst, dass Werksmethoden mehr als einen Objekttyp unterstützen sind in einigen Fällen nützlich; Aber in Ihrem Fall scheint es mehr Ärger zu verursachen, als es wert ist.


PS: Möglicherweise finden Sie einen Aspekt einiger IOC -Behälter interessant. Sie müssen normalerweise konfiguriert werden, und dies umfasst einen Prozess, bei dem Sie konkrete Typen (dh Implementierungsklassen) für abstrakte Schnittstellen registrieren. Zum Beispiel (hier verwenden Autofac):

var builder = new ContainerBuilder();
builder.RegisterType<StringFoo>().As<IFoo<string>>();

Später können Sie eine Objektinstanz eines abstrakten Typs anfordern:

using (var container = builder.Build())
{
    var stringFoo = container.Resolve<IFoo<string>>();
    ...
}

Das Resolve Methode ist der interessante Teil. Sie geben es mit einem abstrakten Typ und verwenden die registrierten Typen ein konkretes Objekt vom Typ StringFoo. Schauen Sie sich das an, wenn es für Sie nicht nach Overkill klingt! :-)

Andere Tipps

Können Sie das Problem beschreiben, das Sie mit diesem Mechanismus lösen? Es gibt höchstwahrscheinlich eine klarere Möglichkeit, sich daran zu nähern.

Bearbeiten

Und ja, der Code riecht. Sie haben für jeden Typ den Raum offen gelassen, außer dass Sie ihn dann wieder auf einen einzelnen Typ beschränken und eine Ausnahme für Laufzeiten generieren. Warum in diesem Fall einen Typparameter haben?

Sie könnten so etwas ausprobieren ...

public static class FooFactory
{
    private static readonly Dictionary<Type, Type> FooTypesLookup;

    static FooFactory()
    {
        FooTypesLookup = (from type in typeof(FooFactory).Assembly.GetExportedTypes()
                          let fooInterface =
                            type.GetInterfaces().FirstOrDefault(
                                x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IFoo<>))
                          where fooInterface != null
                          let firstTypeArgument = fooInterface.GetGenericArguments().First()
                          select new { Type = type, TypeArgument = firstTypeArgument })
            .ToDictionary(x => x.TypeArgument, x => x.Type);
    }

    public static IFoo<T> CreateFoo<T>()
    {
        var genericArgumentType = typeof(T);
        Type closedFooType;
        return FooTypesLookup.TryGetValue(genericArgumentType, out closedFooType)
                ? (IFoo<T>) Activator.CreateInstance(closedFooType)
                : null;
    }
}

Oder noch besser, führen Sie Ihren bevorzugten IOC -Container (Windsor, Strukturkarte usw.) ein und registrieren Sie alle Arten, die IFOO dort implementieren, und lösen Sie sie dann anstelle des Aktivators auf.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top