Wat*_* v2 13 c# covariance contravariance variance
该陈述是什么意思?
在C#中引用和输出参数,不能标记为变体.
1)是否意味着不能做以下事情.
public class SomeClass<R, A>: IVariant<R, A>
{
public virtual R DoSomething( ref A args )
{
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
2)或者它是否意味着我不能拥有以下内容.
public delegate R Reader<out R, in A>(A arg, string s);
public static void AssignReadFromPeonMethodToDelegate(ref Reader<object, Peon> pReader)
{
pReader = ReadFromPeon;
}
static object ReadFromPeon(Peon p, string propertyName)
{
return p.GetType().GetField(propertyName).GetValue(p);
}
static Reader<object, Peon> pReader;
static void Main(string[] args)
{
AssignReadFromPeonMethodToDelegate(ref pReader);
bCanReadWrite = (bool)pReader(peon, "CanReadWrite");
Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
我试过(2)并且它有效.
Eri*_*ert 46
粗略地说,"出"意味着"仅出现在产出位置".
粗略地说,"在"中意味着"仅出现在输入位置".
真实的故事比这更复杂,但选择关键词是因为大部分时间都是如此.
考虑一个接口的方法或委托代表的方法:
delegate void Foo</*???*/ T>(ref T item);
Run Code Online (Sandbox Code Playgroud)
T出现在输入位置吗?是.调用者可以在via项中传递T值; 被叫者Foo可以读取它.因此T不能标记为"out".
T出现在输出位置吗?是.被调用者可以向项目写入新值,然后调用者可以读取该值.因此T不能标记为"in".
因此,如果T出现在"ref"形式参数中,则T不能标记为in或out.
让我们看看出现问题的一些真实例子.假设这是合法的:
delegate void X<out T>(ref T item);
...
X<Dog> x1 = (ref Dog d)=>{ d.Bark(); }
X<Animal> x2 = x1; // covariant;
Animal a = new Cat();
x2(ref a);
Run Code Online (Sandbox Code Playgroud)
好吧我的猫,我们只是做了一个猫皮."出局"不合法.
怎么样"in"?
delegate void X<in T>(ref T item);
...
X<Animal> x1 = (ref Animal a)=>{ a = new Cat(); }
X<Dog> x2 = x1; // contravariant;
Dog d = new Dog();
x2(ref d);
Run Code Online (Sandbox Code Playgroud)
我们只是把一只猫放在一个只能容纳狗的变量中.T也不能标记为"in".
out参数怎么样?
delegate void Foo</*???*/T>(out T item);
Run Code Online (Sandbox Code Playgroud)
?现在T只出现在输出位置.将T标记为"out"是否合法?
很不幸的是,不行."out"实际上与幕后的"ref"没有什么不同."out"和"ref"之间的唯一区别是编译器禁止在被调用者分配之前从out参数读取,并且编译器在被调用者正常返回之前需要赋值.以C#以外的.NET语言编写此接口实现的人可以在初始化之前从该项读取,因此可以将其用作输入.因此,在这种情况下,我们禁止将T标记为"out".这是令人遗憾的,但我们无能为力; 我们必须遵守CLR的类型安全规则.
此外,"out"参数的规则是它们在写入之前不能用于输入.没有规则在写入后不能用于输入.假设我们允许
delegate void X<out T>(out T item);
class C
{
Animal a;
void M()
{
X<Dog> x1 = (out Dog d) =>
{
d = null;
N();
if (d != null)
d.Bark();
};
x<Animal> x2 = x1; // Suppose this were legal covariance.
x2(out this.a);
}
void N()
{
if (this.a == null)
this.a = new Cat();
}
}
Run Code Online (Sandbox Code Playgroud)
我们又一次做了一只猫吠.我们不能让T"出局".
以这种方式输出参数用于输入是非常愚蠢的,但是合法的.
更新:C#7添加in
了一个正式的参数声明,这意味着我们现在有两个in
并且out
意味着两件事; 这会造成一些混乱.让我清楚一点:
in
,out
以及参数列表中ref
的形式参数声明意味着"此参数是调用者提供的变量的别名". ref
表示"被调用者可以读取或写入别名变量,并且必须知道在调用之前分配它.out
表示"被调用者必须在正常返回之前通过别名编写别名变量".它还意味着被调用者在写入之前不能通过别名读取别名变量,因为该变量可能没有明确赋值.in
表示"被调用者可以读取别名变量但不通过别名写入".其目的in
是解决一个罕见的性能问题,即大型结构必须"按值"传递,但这样做很昂贵.作为实现细节,in
参数通常通过指针大小的值传递,这比按值复制更快,但在解除引用时更慢.in
,out
和ref
都是同样的事情; 关于谁在何时读取和写入变量的规则,CLR不知道或不关心. ref
也适用于in
和out
参数.与此相反,in
和out
上型参数声明意味着"这种类型的参数必须不能在协变的方式使用"及"这种类型的参数必须不能在逆变方式使用".
如上所述,我们选择in
并out
使用那些修饰符,因为如果我们看到IFoo<in T, out U>
那么T
用于"输入"位置并U
用于"输出"位置.虽然这不是严格正确的,但在99.9%的用例中它确实是一个有用的助记符.
不幸的是,这interface IFoo<in T, out U> { void Foo(in T t, out U u); }
是非法的,因为它看起来应该有效.它无法工作,因为从CLR验证程序的角度来看,这些都是ref
参数,因此是读写.
这只是一种奇怪的,无意的情况,其中逻辑上应该一起工作的两个功能在实现细节原因上不能很好地协同工作.
归档时间: |
|
查看次数: |
3039 次 |
最近记录: |