2015-04-16 17 views
6

Tôi có mô hình khái niệm sau:Không thể đúc tượng chung

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) 
    { 
     ... 
    } 
} 

Sau đó, tôi cố gắng sử dụng nó trong một cách mà khái niệm tương đương với điều này:

// 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"); 

Tôi muốn sử dụng Get() phương thức trên một cá thể FooService - không có vấn đề gì kiểu FooService được chọn trả về. Tuy nhiên, mã này kết quả trong ngoại lệ sau đây:

Không thể cast đối tượng của loại 'WindowsFormsApplication7.FooService`1 [System.DateTime]' gõ 'WindowsFormsApplication7.FooService`1 [System.Object]'.

Bất kỳ đề xuất nào về cách giải quyết vấn đề này sẽ được đánh giá cao.

Bạn có thể tranh luận, tại sao tôi đã tạo FooService chung ở nơi đầu tiên. Tuy nhiên, điều này được thực hiện để bảo đảm an toàn loại trong một môi trường an toàn kiểu. Tuy nhiên, trong trường hợp cụ thể này, FooService sẽ được sử dụng trong bộ điều khiển Web API để phục vụ các loại Foo khác nhau. Nó sẽ trở lại một phản ứng với Foo của T bỏ qua các loại T.

+0

bạn cũng cần một interface IFooServive whence your c ast là (IFooService ) whence FooService của bạn có thể là một IFooService , hwoever như được chỉ ra dưới đây wont làm việc cho các loại giá trị. – tolanj

Trả lời

7

Tôi nghĩ rằng bạn đã đưa ra câu trả lời đúng:

FooService<double?> không bao giờ có thể được đúc để FooService<object>, và FooService<DateTime> không bao giờ có thể được đúc để FooService<object> .

Sử dụng hiệp phương sai hoặc đối xứng không thay đổi điều đó. Trang https://msdn.microsoft.com/en-us/library/dd997386.aspx tiểu bang: Value types also do not support variance. Và kể từ double?DateTime là các loại giá trị, điều này không bao giờ có thể hoạt động.

+0

tăng gấp đôi? không phải là một loại giá trị. Đó là Nullable , cũng như ông có thể sử dụng DateTime? thay vì DateTime. –

+0

@WojciechKulik, 'Nullable ' là một 'struct' – haim770

+0

@ haim770 oh ... vâng, bạn nói đúng, sai lầm của tôi :-). Dù sao điều này có thể hữu ích: [Tạo biến thể Generic Interface] (https://msdn.microsoft.com/en-us/library/dd997386.aspx) –

0

Tôi đã tạo một ví dụ thực sự hoạt động đối với các loại tham chiếu, nhưng - như được nêu trong @Martin - không bao giờ có thể hoạt động đối với các loại giá trị vì chúng không hỗ trợ phương sai. Ràng buộc where T : class đảm bảo rằng chỉ các loại tham chiếu mới được chấp nhận.

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; 
    } 
} 

Cách sử dụng:

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

Nếu có thể, bạn có thể điều chỉnh mô hình của bạn như thế này:

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); } 
} 

mà sẽ cho phép bạn làm

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

Đó có thể là một lựa chọn tốt mà tôi sẽ xem xét. Cảm ơn –

Các vấn đề liên quan