40

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

一瞬思ったような弱い質問をしたのはなぜですか?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*、構造体または変数へのアドレスをホストする 4 バイト (有効なサイズはアーキテクチャによって異なります) のサイズのスペースを割り当てます。参照を渡すことによるC++では&、ヒープ/スタックからの新しい割り当てはなく、メモリ空間で既に定義されているオブジェクトを操作し、プレーンCのように外部からポインタにメモリをサブ割り当てすることはありませんref.C#では何ですか? .NET VM はプレーン C/C++ のポインターのように処理し、ポインターにGC一時スペースを割り当てますか、それとも C++ の参照のように機能しますか? するrefbool, intコードを切り替えてunsafeアンマネージ スタイルでポインターを渡す方がよいなど、マネージド型でのみ正しく動作するか、または値型でのみ動作しますか?
4

6 に答える 6

41

classC# では、参照型 (つまり、 の代わりに で宣言された型) を参照するものがある場合、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);
    }
}
于 2013-04-25T06:20:12.307 に答える
25

C#の Arefは、C++ 参照に相当します。

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

いくつかの 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);
于 2013-04-25T09:16:35.703 に答える
2

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

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

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

この場合、私は a が無傷のままであると確信しています。数学では、変更は、プログラマーの同意によってここで b が変更されたことを視覚的に伝える代入と見なされるべきです。

a = a.Trim();

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

この代入による変更方法を維持するには、例外的な場合を除き、ref を使用しないようにする必要があります。

于 2013-04-25T06:27:23.083 に答える