C#中的ref和out参数,不能标记为变量

C#中的ref和out参数,不能标记为变量,c#,covariance,contravariance,variance,C#,Covariance,Contravariance,Variance,这句话是什么意思 C#和中的ref和out参数 不能标记为变体 1) 这是否意味着不能做到以下几点 public class SomeClass<R, A>: IVariant<R, A> { public virtual R DoSomething( ref A args ) { return null; } } 公共类SomeClass:ivVariant { 公共虚拟R剂量仪(参考A参数) { 返回null; } } 2

这句话是什么意思

C#和中的ref和out参数 不能标记为变体

1) 这是否意味着不能做到以下几点

public class SomeClass<R, A>: IVariant<R, A>
{
    public virtual R DoSomething( ref A args )
    {
        return null;
    }
}
公共类SomeClass:ivVariant
{
公共虚拟R剂量仪(参考A参数)
{
返回null;
}
}
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();
    }
public委托R读取器(一个参数,字符串s);
公共静态无效AssignReadFromMethodToDelegate(参考读取器前置器)
{
pReader=readfrompion;
}
静态对象ReadFromPaun(Paun p,字符串属性名称)
{
返回p.GetType().GetField(propertyName).GetValue(p);
}
静态读写器;
静态void Main(字符串[]参数)
{
将ReadFromMethod分配给Elegate(参考预订单);
bCanReadWrite=(bool)前置器(paun,“CanReadWrite”);
Console.WriteLine(“按任意键退出…”);
Console.ReadKey();
}

我试过了(2),它成功了。

这意味着你不能有以下声明:

public delegate R MyDelegate<out R, in A>(ref A arg);
public委托R MyDelegate(ref A arg);
编辑:@Eric Lippert纠正了我的错误,这一条仍然是合法的:

public delegate void MyDelegate<R, in A>(A arg, out R s);
public委托void MyDelegate(一个arg,一个out R s);
这实际上是有道理的,因为R泛型参数没有标记为变量,所以它没有违反规则。但是,这一条仍然是非法的:

public delegate void MyDelegate<out R, in A>(A arg, out R s);
public委托void MyDelegate(一个arg,一个out R s);
“out”粗略地说是指“仅出现在输出位置”

粗略地说,“in”表示“仅出现在输入位置”

真实的故事要比这复杂一些,但是选择关键词是因为大多数情况下都是这样

考虑接口的方法或委托表示的方法:

delegate void Foo</*???*/ T>(ref T item);
代表无效Foo(参考T项);
不显示在输入位置?对调用者可以通过项传入一个T值;被叫的Foo可以读到。因此,T不能标记为“out”

不显示在输出位置?对被调用方可以向项写入新值,然后调用方可以读取该值。因此,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);
代表无效X(参考T项);
...
xx1=(参考狗d)=>{d.树皮();}
X x2=x1;//协变的;
动物a=新猫();
x2(参考文献a);
好了,狗,我的猫,我们刚让一只猫叫了起来。“出局”是不合法的

“in”呢

代表无效X(参考T项);
...
X x1=(参考动物a)=>{a=新猫();}
X x2=x1;//逆变;
狗d=新狗();
x2(参考文献d);
我们把一只猫放在一个变量里,这个变量只能容纳狗。T也不能标记为“in”

那么out参数呢

delegate void Foo</*???*/T>(out T item);
代表无效Foo(out T项);
??现在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(); 
    }
}
代表无效X(不含T项);
C类
{
动物a;
void M()
{
X x1=(输出狗d)=>
{ 
d=零;
N();
如果(d!=null)
d、 树皮();
};
x2=x1;//假设这是合法的协方差。
x2(在本节中,a);
}
void N()
{ 
if(this.a==null)
this.a=新猫();
}
}
我们又让一只猫叫了起来。我们不能让T“出局”

以这种方式为输入使用out参数是非常愚蠢的,但却是合法的


更新:C#7在中添加了
作为正式的参数声明,这意味着我们现在既有
输入
又有
输出
,这意味着两件事;这会造成一些混乱。让我澄清一下:

  • 参数列表中正式参数声明中的
    in
    out
    ref
    表示“此参数是调用者提供的变量的别名”
  • ref
    表示“被调用方可以读取或写入别名变量,并且在调用之前必须知道该变量已分配
  • out
    意味着“被调用方必须在别名正常返回之前通过别名写入别名变量”。这还意味着被调用方在写入别名变量之前不得通过别名读取别名变量,因为该变量可能未被明确赋值
  • 中的
    表示“被调用方可以读取带别名的变量,但不能通过别名写入”。在
    的目的是解决一个罕见的性能问题,即必须通过“值”传递大型结构“但这样做很昂贵。作为实现细节,
    中的
    参数通常通过指针大小的值传递,这比按值复制快,但在解引用时速度较慢
  • 从CLR的角度来看,
    in
    out
    ref
    都是一回事;关于谁在什么时间读写什么变量的规则,CLR不知道
    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(); 
        }
    }
    
    interface IFoo<out T>
    {
        T Get();
    
        //bool TryGet(out T value); // doesn't work: Invalid variance: The type parameter 'T' must be invariantly valid on 'IFoo<T>.TryGet(out T)'. 'T' is covariant.
    
        bool TryGet(Action<T> value); // works!
    }
    
    public static class ResultExtension{
        public static bool TryGetValue<T>(this IResult<T> self, out T res) {
            if (self.HasValue) {
                res = self.Value;
                return true;
    
            }
            res = default;
            return false;
        }
    }
    
    public interface IResult<out T>
    {
        bool HasValue { get; }
        T Value { get; }
    }