web-dev-qa-db-ja.com

プロパティを「out」または「ref」パラメータとして渡すことは可能ですか?

プロパティを「out」または「ref」パラメータとして渡すことができますか?そうでない場合はどうしてですか?

例えば.

Person p = new Person(); 

。 。 。

public void Test(out p.Name);
28
Embedd_Khurja

短い答えをお詫びしますが、いいえ、C#言語仕様では許可されていません。

別の質問に対してこれを参照してください answer 試してみるとどうなるかを確認してください。また、制限を回避するために、プロパティを単にパブリックフィールドにするべきではない理由も示しています。

お役に立てれば

編集:なぜですか?

変数をoutまたはrefパラメータに渡すと、実際には変数のアドレス(またはメモリ内の場所)が渡されます。関数内で、コンパイラは変数が実際にどこにあるかを認識し、そのアドレスに値を取得して書き込みます。

プロパティは値のように見えますが、実際には関数のペアであり、それぞれに異なるシグネチャがあります。したがって、プロパティを渡すには、実際には2つの関数ポインタを渡す必要があります。1つはget用、もう1つはset用です。

これは、変数のアドレスとはまったく異なる関数に渡すものです。

つまり、1つの変数アドレスvの2つの関数ポインタ。

更新
C#が私たちのためにこれを処理しないのはなぜですか?

私は違います Eric Lippert ですが、理由を説明します

呼び出している関数のシグネチャは何である必要がありますか?
void MyFn(ref int i)を呼び出したいとしましょう。それをそのままにしておく必要がありますか、それともプロパティも許可するように変更する必要がありますか? void MyFn(prop_ref int i)のような構文に変更された場合、これはかなり役に立たないため、特別なprop_ref修飾子で記述されていないライブラリ関数やサードパーティのコードにプロパティを渡すことはできません。とにかく、あなたはそれが違うべきではないことを示唆していると思います。

ここで、MyFniをCOM関数またはWinAPI呼び出しに渡し、iのアドレスを渡します(つまり、.netの外部、refによって)。プロパティの場合、どのようにしてiのアドレスを取得しますか?プロパティの下にアドレスを取得する実際のintがない場合があります。 VB.Netが行うことをしますか?

Vb.Netコンパイラは、プロパティがByRef引数としてメソッドに渡されたときに検出します。その時点で、変数を宣言し、プロパティを変数にコピーし、変数byrefを渡し、メソッドが呼び出された後、変数をプロパティにコピーして戻します。つまり.

MyFunc(myObject.IntProperty)

になります

Dim temp_i As Integer = myObject.IntProperty
MyFunc(temp_i)
myObject.IntProperty = temp_i

プロパティの副作用はMyFuncが返されるまで発生しません。これにより、あらゆる種類の問題が発生し、非常に微妙なバグが発生する可能性があります。

私の謙虚な意見では、この問題に対するVb.Netソリューションも壊れているので、それを答えとして受け入れるつもりはありません。

C#コンパイラはこれをどのように処理する必要があると思いますか?

38
Binary Worrier

他の人は、C#ではこれを行うことができないと説明しています。 VB.NETでは、オプションstrict/explicitがオンの場合でもcanこれを実行します。

Option Strict On
Option Explicit On
Imports System.Text

Module Test

   Sub Main()
       Dim sb as new StringBuilder
       Foo (sb.Length)
   End Sub

   Sub Foo(ByRef x as Integer)
   End Sub

End Module

上記のコードは、このC#コードと同等です。

using System.Text;

class Test
{
     static void Main()
     {
         StringBuilder sb = new StringBuilder();
         int tmp = sb.Length;
         Foo(ref tmp);
         sb.Length = tmp;
     }

     static void Foo(ref int x)
     {
     }
}

個人的には、C#にこれがないことを嬉しく思います。特に、パラメーターがメソッド内に設定されている場合にプロパティの値に関して、例外がスローされると、水がかなり濁っています。

編集:要求されたように、私がなぜ泥の中の財産を渡すと信じているのかについての私の推論は水を濁します。参照によって正規変数を渡すと、その変数はメソッド内で参照されるたびに評価されます。何らかの理由で値が変更された場合(たとえば、メソッド内の他の作業の副作用として)、その変更はメソッドにすぐに表示されます。 VB.NETで参照によってプロパティを渡す場合は、そうではありません。プロパティゲッターが1回呼び出され、次にプロパティセッターが1回呼び出されます。 「パラメータを使用するたびに、そこから取得して設定するプロパティ-」を渡すのとは異なります。

これは、.NETでフィールドを渡すこととまったく些細なプロパティを渡すことで結果が大きく異なる完全な例です。

Option Strict On
Option Explicit On
Imports System.Text

Class Test

   Dim counter as Integer

   Property CounterProperty As Integer
       Get
           Return counter
       End Get
       Set (ByVal value as Integer)
           counter = value
       End Set
   End Property

   Sub Increment
       counter += 1
   End Sub

   Shared Sub Main()
       Dim t as new Test()
       Console.WriteLine("Counter = {0}", t.counter)
       t.Foo(t.counter)
       Console.WriteLine("Counter = {0}", t.counter)

       t.CounterProperty = 0
       Console.WriteLine("CounterProperty = {0}", t.CounterProperty)
       t.Foo(t.CounterProperty)
       Console.WriteLine("CounterProperty = {0}", t.CounterProperty)
   End Sub

   Sub Foo(ByRef x as Integer)
       x = 5
       Increment
       Increment
       Increment
       x += 1
   End Sub

End Class
12
Jon Skeet

これが許可されないもう1つの理由は、refおよびoutパラメーターがメソッド内で読み取りおよび書き込み可能であるのに対し、プロパティは読み取り専用/書き込み専用である可能性があるためです。

Person
{
    public string Name { get { return "me"; } }
}

さて、これができるとしたらどうでしょうか?

Test(out p.Name);    

public void Test(out string name)
{
    name = "someone else";
}

あなたは今あなたではなく他の誰かですが、それはあなたがgetのみのプロパティNameと結んだ契約に反しています(これがうまくいったとしても)。これは、クラスの読み取り専用メンバーフィールドの場合と同じであり、それらの参照を渡すことはできません。

Person
{
    public readonly string name = "me";
}

Test(out p.name); //not possible.

多分 C#は、メソッドの読み取り専用/書き込み専用引数を考え出すことができます。

public void Test(out settable string name, gettable int count, bool whatever)
{
    name = "someone else";
}

Test(out p.Name, 0, true); // doesnt compile since p.Name is readonly.
2
nawfal

代わりに、このようなことをする必要があります

WhatEverTheType name;

Test(out name);

// Choose one of the following construction

Person p = new Person();
p.Name = name;

Person p = new Person(name);
Person p = new Person(Name => name);
0
Fabian Vilers