web-dev-qa-db-ja.com

C#で変数への参照を返すことは可能ですか?

たとえば、double値への参照を返すことはできますか?

これは私がしたいことです:

ref double GetElement()
{
   ......
   // Calculate x,y,z
   return ref doubleArray[x,y,z];
}

このように使うには

void func()
{
   GetElement()=5.0;
}

それはC++でダブルポインターを返すようなものです...私はそれを書いた方法が間違っていることを知っています。しかし、それを行う正しい方法はありますか?

31
Betamoo

C#の値の型は常に値で渡されます。オブジェクトの参照は常に値で渡されます。 Axarydaxが指摘するように、これは「安全でない」コードで変更されます。

この制約を回避する最も簡単で安全な方法は、何らかの方法でdoubleがオブジェクトにアタッチされていることを確認することです。

public class MyObjectWithADouble {
    public double Element {get; set;} // property is optional, but preferred.
}

...
var obj = new MyObjectWithADouble();
obj.Element = 5.0

また、3次元配列へのdoubleの割り当てをどのように予測するかについて少し混乱していることにも触れておきます。目的を明確にしたい場合があります。

私はあなたが今何をしようとしているのかを少しよく理解していると思います。特定の配列内の値の場所を返し、その場所の値を変更できるようにする必要があります。このパターンは、C#の予想されるパラダイムのいくつかを破るので、あなたが探しているものを達成する他の方法を検討することをお勧めします。しかし、それを行うことが本当に理にかなっている場合は、次のようなことを行います。

public class 3dArrayLocation {public int X; public int Y; public int Z;}

...

public 3dArrayLocation GetElementLocation(...)
{
    // calculate x, y, and z
    return new 3dArrayLocation {X = x, Y = y, Z = z}
}

...

var location = GetElementLocation(...);
doubleArray[location.X, location.Y, location.Z] = 5.0;
6

更新:必要な機能がC#7でサポートされるようになりました。


CLR型システムはrefを返すメソッドをサポートします。私は、必要な機能をサポートするC#コンパイラーの実験的なプロトタイプを作成しました。 (プロトタイプはref型のローカル変数も実装しますが、CLR型システムではref型のフィールドは無効です。)

私がプロトタイプ用に選択した構文を正確に使用しました。つまり、偉大な心が同じように考えるか、愚か者が決して変わらないということです。

プロトタイプは非常にうまく機能しますが、これによってバーがC#言語の次のバージョンの機能になることはほとんどありません。非常に少数のお客様がこの機能を必要としています。実装にかなりの費用がかかります。より重要な機能を備えているかぎり、リストがあります。このようなことを型システムに追加せずにこの種類のものを機能させる他の方法があります。これらはすべて、機能を実行する際の大きな「反対点」です。

たとえば、デリゲートのペアを作成できます。

struct Ref<T>
{
    private readonly Func<T> getter;
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}

var arr = new int[10];
var myref = new Ref<int>(()=>arr[1], x=>arr[1]=x);
myref.Value = 10;
Console.WriteLine(myref.Value);

これは、refリターンで実装された同じ機能よりもかなり低速ですが、refが正当でない場所でRef<T>を作成できるという利点があります。たとえば、フィールドにRef<T>を格納できますが、refを返すメソッドではできません。

なぜあなたがneedref-returningメソッドを必要とするのかについて本当に素晴らしい説得力のあるシナリオがある場合、私はloveそれについて聞いてください。実際のシナリオが多ければ多いほど、そのような機能が仮想の言語の将来のバージョンに実装される可能性が高くなります。

関連する質問も参照してください。

C++のようなC#関数内で参照を使用できますか?

C#が参照の戻りをサポートしないのはなぜですか?

そして主題に関する私のブログ投稿:

http://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/

43
Eric Lippert

いいえ、C#ではできません。参照によってのみパラメーターを渡すことができます。

ただし、プロパティを使用して同じ結果を得ることができます。

double Element
{
    get { return doubleArray[x,y,z]; }
    set { doubleArray[x,y,z] = value; }
}

void func()
{
   Element = 5.0;
}
8
Thomas Levesque

あなたはできます安全でないコード でそれを行い、doubleへのポインタを返すメソッドがあります:

unsafe double* GetElementP(){
...
}
6
Axarydax

少し考えた後、次のような関数で設定する値を送信してみませんか。

void SetElement(double value)
{
   ......
   // Calculate x,y,z
   doubleArray[x,y,z]=value;
}

そしてそれを使う:

void func()
{
   SetElement(5.0);
}

しかし、私はあなたの役に立つ答えの1つを選択します...

1
Betamoo