我不清楚为什么下面的代码片段不是covarent?
public interface IResourceColl<out T> : IEnumerable<T> where T : IResource {
int Count { get; }
T this[int index] { get; }
bool TryGetValue( string SUID, out T obj ); // Error here?
}
错误1无效差异:类型参数"T"必须是不变量在"IResourceColl.TryGetValue(string,out T)"上有效T’是协变。
我的接口只在输出位置使用模板参数。我可以很容易地将这些代码重构为类似的代码
public interface IResourceColl<out T> : IEnumerable<T> where T : class, IResource {
int Count { get; }
T this[int index] { get; }
T TryGetValue( string SUID ); // return null if not found
}
但我试图理解我的原始代码是否真的违反了协方差,或者这是编译器还是.NET对协方差的限制。
问题确实在这里:
bool TryGetValue( string SUID, out T obj ); // Error here?
您将obj标记为out
参数,这仍然意味着您正在传入obj
,因此它不能是协变的,因为您既传入T
类型的实例又返回它。
编辑:
Eric Lippert说,这比任何人都好,我引用了他对"C#中的ref和out参数,不能标记为变量"的回答,并引用了他关于out
参数的话:
将T标记为"out"是否合法?遗憾的是没有。"出局"实际上和幕后的"裁判"没什么不同。唯一"out"one_answers"ref"的区别在于编译器禁止在被调用者分配out参数之前读取该参数,以及编译器在被调用者返回之前需要赋值正常情况下在C#以外的.NET语言将能够在它已初始化,因此可以用作输入。我们因此,在这种情况下禁止将T标记为"out"。很遗憾,但我们对此无能为力;我们必须遵守型号安全规则CLR的。
以下是使用扩展方法的可能解决方法。从实现者的角度来看不一定很方便,但用户应该很高兴:
public interface IExample<out T>
{
T TryGetByName(string name, out bool success);
}
public static class HelperClass
{
public static bool TryGetByName<T>(this IExample<T> @this, string name, out T child)
{
bool success;
child = @this.TryGetByName(name, out success);
return success;
}
}
public interface IAnimal { };
public interface IFish : IAnimal { };
public class XavierTheFish : IFish { };
public class Aquarium : IExample<IFish>
{
public IFish TryGetByName(string name, out bool success)
{
if (name == "Xavier")
{
success = true;
return new XavierTheFish();
}
else
{
success = false;
return null;
}
}
}
public static class Test
{
public static void Main()
{
var aquarium = new Aquarium();
IAnimal child;
if (aquarium.TryGetByName("Xavier", out child))
{
Console.WriteLine(child);
}
}
}
它违反了协方差,因为提供给输出参数的值必须与输出参数声明的类型完全相同。例如,假设T
是一个字符串,协方差意味着可以进行
var someIResourceColl = new someIResourceCollClass<String>();
Object k;
someIResourceColl.TryGetValue("Foo", out k); // This will break because k is an Object, not a String
检查这个小例子,你就会明白为什么不允许:
public void Test()
{
string s = "Hello";
Foo(out s);
}
public void Foo(out string s) //s is passed with "Hello" even if not usable
{
s = "Bye";
}
out
意味着在执行离开方法之前必须明确地分配s
,相反,在方法体中明确地分配之前不能使用s
。这似乎与协方差规则兼容。但是,在调用该方法之前,没有什么可以阻止您在调用站点分配s
。该值被传递给方法,这意味着即使它不可用,您也会有效地将定义类型的参数传递给该方法,这违反了协方差的规则,即泛型类型只能用作方法的返回类型。