2015-04-16 16 views
6

Ich habe folgendes konzeptionelles Modell werfen:kann nicht generisches Objekt

public interface IFoo<out T> 
{ 
    T Data { get; } 
} 

public struct Foo<T> : IFoo<T> 
{ 
    public Foo(T data) : this() 
    { 
     Data = data; 
    } 

    public T Data { get; private set; } 
} 

public class FooService<T> 
{ 
    ... 

    public Foo<T> Get(string id) 
    { 
     ... 
    } 
} 

Dann versuche ich es in einer Weise zu verwenden, die vom Konzept her entspricht dies:

// Create and register a few FooService instances 
ServiceLocator.Register(new FooService<DateTime>(), "someServiceId"); 
ServiceLocator.Register(new FooService<double?>(), "anotherServiceId"); 

// Retrieve a particular FooService instance and call the Get method 
var fooService = (FooService<object>)ServiceLocator.Get("someServiceId"); 
var foo = fooService.Get("someFooId"); 

ich das verwenden möchten Get() -Methode für eine FooService-Instanz - unabhängig davon, welchen Typ die ausgewählte FooService-Instanz zurückgibt. Doch dieser Code ergibt die folgende Ausnahme:

Kann nicht das Objekt von Typ 'WindowsFormsApplication7.FooService`1 [System.DateTime]' eingeben 'WindowsFormsApplication7.FooService`1 [System.Object]'.

Irgendwelche Vorschläge zur Lösung dieses Problems würden sehr geschätzt werden.

Sie könnten argumentieren, warum ich den FooService in erster Linie generisch gemacht habe. Dies geschieht jedoch, um die Typensicherheit in einer typensicheren Umgebung zu gewährleisten. In diesem speziellen Fall sollte der FooService jedoch in einem Web-API-Controller verwendet werden, um verschiedene Arten von Foo zu bedienen. Es wird eine Antwort mit Foo von T gibt die Art von T. abgesehen

+0

Sie auch ein Schnittstelle IFooServive woher Ihr c ast ist (IFooService ), woher dein FooService ein IFooService sein kann, wie unten erwähnt, wird dies nicht für Werttypen funktionieren. – tolanj

Antwort

7

Ich glaube, Sie bereits die richtige Antwort gegeben haben:

FooService<double?> kann nie zu FooService<object> gegossen wird, und FooService<DateTime> kann nie zu FooService<object> gegossen werden .

Mit Kovarianz oder Kontravarianz ändert sich das nicht. Die Seite https://msdn.microsoft.com/en-us/library/dd997386.aspx besagt: Value types also do not support variance. Und da double? und DateTime Werttypen sind, kann dies nie funktionieren.

+0

doppelt? ist kein Werttyp. Es ist Nullable , auch kann er DateTime verwenden? anstelle von DateTime. –

+0

@WojciechKulik, 'Nullable ' ist ein 'struct' – haim770

+0

@ haim770 oh ... ja, du hast Recht, mein Fehler :-). Wie auch immer, das kann hilfreich sein: [Erstellen generischer Variant-Schnittstellen] (https://msdn.microsoft.com/en-us/library/dd997386.aspx) –

0

Ich machte ein Beispiel, das tatsächlich für Referenztypen funktioniert, aber - wie von @Martin angegeben - kann nie für Werttypen arbeiten, da sie Varianz nicht unterstützen. Die Bedingung where T : class stellt sicher, dass nur Referenztypen akzeptiert werden.

public interface IFoo<out T> where T : class 
{ 
    T Data { get; } 
} 

public struct Foo<T> : IFoo<T> where T : class 
{ 
    public Foo(T data) : this() 
    { 
     Data = data; 
    } 

    public T Data { get; private set; } 
} 

public interface IFooService<out T> where T : class 
{ 
    IFoo<T> Get(string id); 
} 

public class FooService<T> : IFooService<T> where T : class 
{ 
    public IFoo<T> Get(string id) 
    { 
     return null; 
    } 
} 

Verbrauch:

ServiceLocator.Register(new FooService<Bar>(), "ServiceId"); 
// OK because Bar is a reference type 
var fooService = (IFooService<object>)ServiceLocator.Get("ServiceId"); 
var foo = fooService.Get("FooId"); 
1

Wenn möglich, können Sie Ihr Modell wie folgt einstellen könnte:

public interface IFoo 
{ 
    object Data { get; } 
} 

public interface IFoo<T> : IFoo 
{ 
    new T Data { get; } 
} 

public class Foo<T> : IFoo<T> 
{ 
    public Foo(T data) { Data = data; } 
    public T Data { get; private set; } 
    object IFoo.Data { get { return Data; } } 
} 

public interface IFooService 
{ 
    IFoo Get(string id); 
} 

public interface IFooService<T> : IFooService 
{ 
    new IFoo<T> Get(string id); 
} 

public class FooService<T> : IFooService<T> 
{ 
    public IFoo<T> Get(string id) { return null; } 
    IFoo IFooService.Get(string id) { return Get(id); } 
} 

die es Ihnen ermöglichen würde tun müssen,

ServiceLocator.Register(new FooService<DateTime>(), "someServiceId"); 
ServiceLocator.Register(new FooService<double?>(), "anotherServiceId"); 

// Retrieve a particular FooService instance and call the Get method 
IFooService fooService = (IFooService)ServiceLocator.Get("someServiceId"); 
IFoo foo = fooService.Get("someFooId"); 
object data = foo.Data; 
+0

Das könnte eine gute Alternative sein, die ich in Betracht ziehen werde. Vielen Dank –

Verwandte Themen