web-dev-qa-db-ja.com

C#refは、C / C ++のポインターまたはC ++の参照のようなものですか?

私はrefを使用していますが、明確に理解できません「C/C++のようにポインターのようですか、それともC++の参照のようですか?」

ちょっと考えたのに、なぜそんなに弱い質問をしたのですか?なぜなら、C#/。NETの書籍、msdnを読んでいるとき、またはC#開発者と話をしているとき、次の理由で混乱しているからです。

  • C#開発者は、関数の引数にrefを使用しないことを提案します。e.g. ...(ref Type someObject)は彼らにとって良い匂いがなく、彼らは...(Type someObject)を提案します、私は本当にこの提案を明確に理解していません。私が聞いた理由:オブジェクトのコピーを操作し、参照などによってメモリを破損しないように、戻り値として使用することをお勧めします。多くの場合、DB接続オブジェクトに関するこのような説明を聞きます。私の単純なC/C++の経験と同様に、参照を使用することがC#で悪いことである理由を本当に理解していませんか?私はオブジェクトの寿命とそのメモリの割り当て/再割り当てなどを制御します。本やフォーラムで読むのはit's bad, because you can corrupt your connection and cause a memory leak by a reference loseのみであるため、オブジェクトの寿命を制御します。本当に欲しいものを手動で制御できます。どうして悪いの?
  • 最近、さまざまな本を読んだり、さまざまな人と話をしたりしても、refポインター(*)またはC++の&のような参照が明確に理解できませんか?私が覚えているように、C/C++のポインターは常にvoid* type-4バイト(有効なサイズはアーキテクチャーによって異なります)のサイズのスペースを割り当てます。 C++では、参照&を渡すことにより、ヒープ/スタックからの新しい割り当てはなく、メモリ空間で既に定義されたオブジェクトを操作します。 C#のref? .NET VMプレーンC/C++のポインターのように処理し、そのGCはポインターに一時スペースを割り当てますか、C++の参照のような動作をしますか?refマネージ型でのみ正しく動作するか、またはbool, intなどの値型に対してのみ動作します。unsafeコードを切り替えて、アンマネージスタイルでポインターを渡す方が良いでしょうか。
36
Secret

C#では、参照型(つまり、classの代わりにstructで宣言された型)を参照するものが表示される場合、本質的に常にポインターを介してオブジェクトを処理しています。 C++ではすべてがデフォルトで値型ですが、C#ではすべてがデフォルトで参照型です。

C#パラメータリストで「ref」と言うとき、実際に言っているのは、「ポインタへのポインタ」のようなものです。メソッドでは、メソッドを呼び出すコードで、オブジェクトのコンテンツではなく、オブジェクト自体への参照を置き換えることを言っています。

それがあなたの意図でない限り、参照型を直接渡すだけです。 C#では、参照型を渡すことは安価です(C++で参照を渡すのに似ています)。

C#の値型と参照型の違いを学習/理解します。それらはその言語の主要な概念であり、C#の土地でC++オブジェクトモデルを使用することを考えようとすると、物事は本当に混乱するでしょう。

以下は、本質的に意味的に同等のプログラムです。

#include <iostream>

class AClass
{
    int anInteger;
public:
    AClass(int integer)
        : anInteger(integer)
    {  }

    int GetInteger() const
    {
        return anInteger;
    }

    void SetInteger(int toSet)
    {
        anInteger = toSet;
    }
};

struct StaticFunctions
{
    // C# doesn't have free functions, so I'll do similar in C++
    // Note that in real code you'd use a free function for this.

    static void FunctionTakingAReference(AClass *item)
    {
        item->SetInteger(4);
    }

    static void FunctionTakingAReferenceToAReference(AClass **item)
    {
        *item = new AClass(1729);
    }
};

int main()
{
    AClass* instanceOne = new AClass(6);
    StaticFunctions::FunctionTakingAReference(instanceOne);
    std::cout << instanceOne->GetInteger() << "\n";

    AClass* instanceTwo;
    StaticFunctions::FunctionTakingAReferenceToAReference(&instanceTwo);
    // Note that operator& behaves similar to the C# keyword "ref" at the call site.
    std::cout << instanceTwo->GetInteger() << "\n";

    // (Of course in real C++ you're using std::shared_ptr and std::unique_ptr instead,
    //  right? :) )
    delete instanceOne;
    delete instanceTwo;
}

C#の場合:

using System;

internal class AClass
{
    public AClass(int integer)
        : Integer(integer)
    {  }

    int Integer { get; set; }
}

internal static class StaticFunctions
{
    public static void FunctionTakingAReference(AClass item)
    {
        item.Integer = 4;
    }

    public static void FunctionTakingAReferenceToAReference(ref AClass item)
    {
        item = new AClass(1729);
    }
}

public static class Program
{
    public static void main()
    {
        AClass instanceOne = new AClass(6);
        StaticFunctions.FunctionTakingAReference(instanceOne);
        Console.WriteLine(instanceOne.Integer);

        AClass instanceTwo  = new AClass(1234); // C# forces me to assign this before
                                                // it can be passed. Use "out" instead of
                                                // "ref" and that requirement goes away.
        StaticFunctions.FunctionTakingAReferenceToAReference(ref instanceTwo);
        Console.WriteLine(instanceTwo.Integer);
    }
}
37
Billy ONeal

C#のrefは、C++参照と同等です。

  • それらの意図は参照渡しです
  • ヌル参照はありません
  • 初期化されていない参照はありません
  • 参照を再バインドすることはできません
  • 参照をつづるとき、実際には参照される変数を示しています

いくつかのC++コード:

void foo(int& x)
{
    x = 42;
}
// ...
int answer = 0;
foo(answer);

同等のC#コード:

void foo(ref int x)
{
    x = 42;
}
// ...
int answer = 0;
foo(ref answer);
22
fredoverflow

C#のすべての参照は、C++のポインターとC#の参照がC++の&と同じであるため、ヒープ上のオブジェクトへのポインターです。

Refを避けるべき理由は、メソッドのソースを持っていない人はデータの損失をもたらすかどうかを知らない可能性があるため、C#はメソッドがパラメーターで渡されるオブジェクトを変更しないという基本に基づいています。

String a = "  A  ";
String b = a.Trim();

この場合、私はaがそのまま残っていると確信しています。数学では、変更は、bがプログラマの同意によってここで変更されることを視覚的に示す割り当てと見なされる必要があります。

a = a.Trim();

このコードはそれ自体を変更し、コーダーはそれを認識します。

割り当てによるrefの変更方法を保持するには、例外的な場合を除き、refを避ける必要があります。

2
Akash Kava

これは、廃棄/イベントの悪夢のようです。イベントの登録対象のオブジェクトがあり、それを参照によって関数に渡し、その参照が再割り当てされる場合、disposeが呼び出されるか、プログラムが終了するまでメモリが割り当てられます。 disposeが呼び出された場合、オブジェクトに登録されているすべてのイベントは登録されなくなり、登録されたすべてのイベントも登録されなくなります。誰かがこれをどのようにまっすぐに保つでしょうか?気が狂わなければ、メモリアドレスを比較して正気に戻すことができると思います。

1
clarkewu

C#にはC++ポインターに相当するものはなく、参照に対して機能します。 refは、間接性のレベルを追加します。値型引数を参照にし、参照型と共に使用すると、参照への参照になります。

要するに、メソッド呼び出しの外部で値型への変更を運ぶことができます。参照タイプの場合、元の参照をまったく異なるオブジェクトに置き換えることができます(オブジェクトの内容を変更するだけではありません)。メソッド内のオブジェクトを再初期化する場合に使用できます。そのための唯一の方法は、オブジェクトを再作成することです。私はそのようなアプローチを避けようとしますが。

したがって、あなたの質問に答えるrefは、参照へのC++参照のようになります。

編集

上記は安全なコードに当てはまります。ポインターは安全ではないC#に存在し、非常に特殊な場合に使用されます。

0
Maciej