我一直在研究 c# 7 ref return功能,并在运行其中一个测试片段时遇到了意外情况。
以下代码:
namespace StackOverflow
{
using System;
public interface IXTuple<T>
{
T Item1 { get; set; }
}
public class RefXTuple<T> : IXTuple<T>
{
T _item1;
public ref T Item1Ref
{
get => ref _item1;
}
public T Item1
{
get => _item1;
set => _item1 = value;
}
}
public struct ValXTuple<T> : IXTuple<T>
{
T _item1;
public T Item1
{
get => _item1;
set => _item1 = value;
}
}
public class UseXTuple
{
public void Experiment1()
{
try
{
RefXTuple<ValXTuple<String>> refValXTuple = new RefXTuple<ValXTuple<String>> {Item1 = new ValXTuple<String> {Item1 = "B-"}};
dynamic dynXTuple = refValXTuple;
refValXTuple.Item1Ref.Item1 += "!";
Console.WriteLine($"Print 1: {refValXTuple.Item1.Item1 == "B-!"}");
Console.WriteLine($"Print 2: {dynXTuple.Item1.Item1 == "B-!"}");
refValXTuple.Item1Ref.Item1 += "!";
Console.WriteLine($"Print 3: {refValXTuple.Item1Ref.Item1 == "B-!!"}");
Console.WriteLine($"Print 4: {dynXTuple.Item1Ref.Item1 == "B-!!"}");
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
给出以下打印输出:
Print 1: True
Print 2: True
Print 3: True
System.InvalidCastException: The result type 'StackOverflow.ValXTuple`1[System.String]&' of the dynamic binding produced by binder 'Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder' is not compatible with the result type 'System.Object' expected by the call site.
at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at StackOverflow.UseXTuple.Experiment1() in C:\Repo\TestBed.Lib\Features\ReturnRefByDynamic.cs:line 52
这有点出乎意料。我希望在打印输出中看到以下行而不是异常:
Print 4: True
通过动态变量调用返回 ref 的属性时抛出异常。我花了一些时间寻找答案(例如这里C# Reference),但找不到任何可以证明这种行为合理的东西。我会很感激你在这方面的帮助。
很明显,通过强类型变量调用可以正常工作(“打印 3”行),而通过动态变量的相同调用会引发异常。在这种情况下,我们可以认为通过动态变量调用是安全且可预测的吗?是否存在动态调用产生与其强类型对应物大不相同的结果的任何其他情况?