无法投射通用对象
Unable to cast generic object
我有以下概念模型:
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)
{
...
}
}
然后我尝试以概念上等同于此的方式使用它:
// 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");
我想在 FooService 实例上使用 Get() 方法 - 无论所选 FooService 实例是什么类型 returns。但是,此代码会导致以下异常:
Unable to cast object of type 'WindowsFormsApplication7.FooService`1[System.DateTime]' to type 'WindowsFormsApplication7.FooService`1[System.Object]'.
如有任何关于如何解决此问题的建议,我们将不胜感激。
您可能会争辩说,为什么我首先将 FooService 设为泛型。但是,这样做是为了在类型安全的环境中确保类型安全。但是,在这种特殊情况下,FooService 应在 Web API 控制器中使用以服务于各种类型的 Foo。它应 return 响应为 T 的 Foo 而不管 T 的类型。
我想你已经给出了正确答案:
FooService<double?>
永远无法转换为 FooService<object>
,并且
FooService<DateTime>
永远无法转换为 FooService<object>
。
使用协变或逆变不会改变这一点。页面 https://msdn.microsoft.com/en-us/library/dd997386.aspx 指出:Value types also do not support variance.
并且由于 double?
和 DateTime
是值类型,所以这永远行不通。
我做了一个实际适用于引用类型的示例,但是 - 如@Martin 所述 - 永远不适用于值类型,因为它们不支持变化。 where T : class
约束确保只接受引用类型。
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;
}
}
用法:
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");
如果可能,您可以这样调整您的模型:
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); }
}
这将使您能够做到
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;
我有以下概念模型:
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)
{
...
}
}
然后我尝试以概念上等同于此的方式使用它:
// 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");
我想在 FooService 实例上使用 Get() 方法 - 无论所选 FooService 实例是什么类型 returns。但是,此代码会导致以下异常:
Unable to cast object of type 'WindowsFormsApplication7.FooService`1[System.DateTime]' to type 'WindowsFormsApplication7.FooService`1[System.Object]'.
如有任何关于如何解决此问题的建议,我们将不胜感激。
您可能会争辩说,为什么我首先将 FooService 设为泛型。但是,这样做是为了在类型安全的环境中确保类型安全。但是,在这种特殊情况下,FooService 应在 Web API 控制器中使用以服务于各种类型的 Foo。它应 return 响应为 T 的 Foo 而不管 T 的类型。
我想你已经给出了正确答案:
FooService<double?>
永远无法转换为 FooService<object>
,并且
FooService<DateTime>
永远无法转换为 FooService<object>
。
使用协变或逆变不会改变这一点。页面 https://msdn.microsoft.com/en-us/library/dd997386.aspx 指出:Value types also do not support variance.
并且由于 double?
和 DateTime
是值类型,所以这永远行不通。
我做了一个实际适用于引用类型的示例,但是 - 如@Martin 所述 - 永远不适用于值类型,因为它们不支持变化。 where T : class
约束确保只接受引用类型。
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;
}
}
用法:
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");
如果可能,您可以这样调整您的模型:
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); }
}
这将使您能够做到
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;