如何使用ref参数调用方法时删除ref关键字限制?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用ref参数调用方法时删除ref关键字限制?相关的知识,希望对你有一定的参考价值。

请注意,这个问题不是要打破语言对开发的限制,而只是为了研究可能性。

C#的语言设计者希望调用者知道他们在做什么,所以经常你不能简单地删除调用时的ref(或out)关键字,比如foo(ref bar)foo(bar)。但是,如果您真的想要打破语言限制,无论设计理念如何?

提案:

var (a, b) = ((5, 6), (7, 8));
a.Swap(b);
a.Item1.Swap(a.Item2);
b.Item1.Swap(b.Item2);
Console.WriteLine($"a=({a.Item1},{a.Item2}),b=({b.Item1},{b.Item2})");

哪个输出

a=(8,7),b=(6,5)
答案

在没有任何其他关键字的情况下通过引用传递参数的唯一解决方案是C#7.2中的The in modifier on parameters。它在调用时不需要显式的in关键字,因为它是为被调用者设计的,不会将任何值赋给by-ref参数,这样调用者就不需要被告知他们正在做什么。但实际上in只不过是ref,其属性标志着它是只读的,C#编译器不会允许任何赋值。但是当引用通过除赋值之外的某种方式传递(包括强类型传递)时,可以绕过此限制。

public class RefUnlocker
{
    private delegate ref T InToRef<T>(in T arg) where T : struct;

    private static ref T RefWrapper<T>(ref T arg) where T : struct => ref arg;

    private static readonly MethodInfo _refWrapper = typeof(RefUnlocker)
        .GetMethod(nameof(RefWrapper), BindingFlags.Static | BindingFlags.NonPublic);

    public static ref T Unlock<T>(in T arg) where T : struct
        => ref ((InToRef<T>)Delegate.CreateDelegate(typeof(InToRef<T>),
            _refWrapper.MakeGenericMethod(typeof(T))))(arg);
}

RefUnlocker.Unlock方法通过从方法动态生成委托来帮助将in参数转换为ref参数(因为in参数也是与ref参数相同的ByRef类型,因此方法签名与委托匹配),这意味着您可以简单地编写像Swap的方法

public static void Swap<T>(ref this T op1, in T op2)
    where T : struct
{
    var temp = op1;
    op1 = op2;
    RefUnlocker.Unlock(op2) = temp;
}

(注意,在扩展方法中作为this参数的ref参数在调用时不需要'ref'关键字,因为它不像参数那样传递)

然后你可以在没有任何其他关键字的情况下调用它

var (a, b) = ((5, 6), (7, 8));
a.Swap(b);
a.Item1.Swap(a.Item2);
b.Item1.Swap(b.Item2);
Console.WriteLine($"a=({a.Item1},{a.Item2}),b=({b.Item1},{b.Item2})");
// Outputs: a=(8,7),b=(6,5)

以上是关于如何使用ref参数调用方法时删除ref关键字限制?的主要内容,如果未能解决你的问题,请参考以下文章

ref 与 out

C# ref与out关键字区别

ref关键字的作用

C#中关键字ref和out的区别

ref关键字的用法

C#:关键字out和ref之间的区别