4

私は c# 7 ref return機能を調べていて、テスト スニペットの 1 つを実行しているときに予期しないシナリオに遭遇しました。

次のコード:

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# リファレンス)、そのような動作を正当化できるものは見つかりませんでした。これについてあなたの助けをいただければ幸いです。

強力な型付き変数を介した呼び出しが正常に機能することは明らかです (「Print 3」行) が、動的変数を介した同じ呼び出しは例外をスローします。この状況で、動的変数を介した呼び出しが安全で予測可能であると見なすことができますか? 動的呼び出しが強い型付けの対応物とは大きく異なる結果を生成する他のシナリオはありますか?

4

1 に答える 1