来自Java世界,使用泛型和C#编程通常很头疼。像这样:
interface ISomeObject { }
class SomeObjectA : ISomeObject { }
class SomeObjectB : ISomeObject { }
interface ISomething<T> where T : ISomeObject
{
T GetObject();
}
class SomethingA : ISomething<SomeObjectA>
{
public SomeObjectA GetObject() { return new SomeObjectA(); }
}
class SomethingB : ISomething<SomeObjectB>
{
public SomeObjectB GetObject() { return new SomeObjectB(); }
}
class SomeContainer
{
private ISomething<ISomeObject> Something;
public void SetSomething<T>(ISomething<T> s) where T : ISomeObject
{
Something = (ISomething<ISomeObject>)s;
}
}
class TestContainerSomething
{
static public void Test()
{
SomeContainer Container = new SomeContainer();
Container.SetSomething<SomeObjectA>(new SomethingA());
}
}
结果为InvalidCastException
Something = (ISomething<ISomeObject>)s;
。在Java中,这可以工作,我甚至可以使用泛型通配符<?>
(如果所有其他方法都失败了)。这在C#中是不可能的。
虽然这只是我用来解释问题的一个例子,但如何消除这种异常呢?唯一的主要限制是SomeContainer
不能是通用类
** 注意 **:关于这个问题有很多问题,但是没有一个(我能找到)解决非泛型类中的泛型类成员。
** 更新 **
在方法SetSomething
中,我添加了以下几行:
Console.WriteLine(s.GetType().IsSubclassOf(typeof(ISomething<SomeObjectA>)));
Console.WriteLine(s.GetType().ToString() + " : " + s.GetType().BaseType.ToString());
foreach (var i in s.GetType().GetInterfaces())
{
Console.WriteLine(i.ToString());
}
令我惊讶的输出
False
SomeThingA : System.Object
ISomething`1[SomeObjectA]
这就是我得到这个例外的原因吗?
答案 0 :(得分:5)
ISomething
只有返回T的方法,那么Out关键字将是一个修正
interface ISomething<out T> where T : ISomeObject
创建通用接口时,可以指定具有不同类型参数的接口实例之间是否存在隐式转换。
它被称为Covariance and Contravariance
Eric Lippert有一个很好的series of articles为什么我们需要考虑这个问题,这里使用了interface variance
这是我的代码,它按我的预期工作
interface ISomeObject { }
class SomeObjectA : ISomeObject { }
class SomeObjectB : ISomeObject { }
interface ISomething<out T> where T : ISomeObject
{
T GetObject();
}
class SomethingA : ISomething<SomeObjectA>
{
public SomeObjectA GetObject() { return new SomeObjectA(); }
}
class SomethingB : ISomething<SomeObjectB>
{
public SomeObjectB GetObject() { return new SomeObjectB(); }
}
class SomeContainer
{
private ISomething<ISomeObject> Something;
public void SetSomething<T>(ISomething<T> s) where T : ISomeObject
{
Something = (ISomething<ISomeObject>)s;
}
}
class TestContainerSomething
{
static public void Test()
{
SomeContainer Container = new SomeContainer();
Container.SetSomething<SomeObjectA>(new SomethingA());
}
}
答案 1 :(得分:2)
有时让通用接口实现非通用接口来规避丢失的<?>
interface ISomething
{
object GetObject();
}
interface ISomething<T> : ISomething
where T : ISomeObject
{
T GetObject();
}
public class SomeImplementation<T> : ISomething<T>
{
public T GetObject()
{
...
}
object ISomething.GetObject()
{
return this.GetObject(); // Calls non generic version
}
}
然后可以使用非通用接口
键入集合var list = new List<ISomething>();
list.Add(new SomeImplementation<string>());
list.Add(new SomeImplementation<int>());