最近、メソッドに入力専用パラメーターを使用できるかどうか尋ねられました。質問に対する私の即座の答えは「いいえ」でしたが、もう一度考えてみると、正しい情報を共有したかどうかわかりません。また、なぜ .NET では入力のみのパラメーター (Contravariance の実装時に使用するようなもの) を使用できないのかについて、合理的な説明がありません。
3 に答える
パラメータの受け渡しについては、こちらを参照してください。
「入力のみ」と言うときは、値で渡されたパラメーターを参照していると思います。
C# では、通常、パラメーターを値で渡します。オブジェクトを渡すときは、「ポインター」を値で渡します。したがって、ポインターが指している対象を変更しても、外部からの変数は変更されません。ただし、オブジェクトの内容を変更すると、両方の「ポインター」が同じオブジェクトを指しているため、両方に変更が表示されます。
したがって、すべてを「値」で渡すことができますが、オブジェクトを渡す場合は、呼び出しているメソッドによって変更されないように、オブジェクトのコピーを作成する必要があります。
私の言いたいことを理解するために、ここで小さなテスト例を見ることができます。
[TestMethod]
public void TestMethod()
{
var john = new Person() { Name = "John" };
var tom = new Person() { Name = "Tom" };
var person1 = john;
var person2 = tom;
SwapPersonsMethod1(person1, person2);
//Person1 is still John
Assert.AreEqual(person1, john);
//Person2 is still Tom
Assert.AreEqual(person2, tom);
SwapPersonsMethod2(ref person1, ref person2);
//Person1 is still Tom
Assert.AreEqual(person1, tom);
//Person2 is still John
Assert.AreEqual(person2, john);
UpdateName(person1, "Tomas");
//Person1 is pointing to var tom, and its name now is Tomas.
Assert.AreEqual(person1.Name, "Tomas");
Assert.AreEqual(tom.Name, "Tomas");
SwapPersonsMethod3(person1, person2, "Jonathan");
//Person1 is still Tom
Assert.AreEqual(person1, tom);
//Person2 is still John
Assert.AreEqual(person2, john);
//John name has changed to Jonathan
Assert.AreEqual(person2.Name, "Jonathan");
Assert.AreEqual(john.Name, "Jonathan");
}
private void UpdateName(Person person, string name)
{
person.Name = name;
}
private void SwapPersonsMethod1(Person person1, Person person2)
{
var aux = person1;
person1 = person2;
person2 = aux;
}
private void SwapPersonsMethod2(ref Person person1, ref Person person2)
{
var aux = person1;
person1 = person2;
person2 = aux;
}
private void SwapPersonsMethod3(Person person1, Person person2, string name)
{
var aux = person1;
person1 = person2;
person2 = aux;
UpdateName(person1, name);
}
public class Person
{
public string Name { get; set; }
}
私はここで大胆な飛躍を遂げていますが、呼び出された関数がパラメーターの内容を変更するのを防ぐことができるかどうかを尋ねているようです (呼び出し元から見た場合)。
例: ボブがプログラミングを好まないようにするため
public void Run()
{
Person Bob = New Person();
Bob.LikesToProgram = false;
Helper(Bob);
Console.WriteLine("Bob likes to program = " + Bob.LikesToProgram);
//Output: Turns out Bob likes to program!!!
}
public void Helper(Person input)
{
input.LikesToProgram = true;
}
あなたの質問に対する答えは です。. . それはパラメータのタイプに依存します!
そして
、パターンを介していつでも不変の型を作成できます!
値渡し
これは、オブジェクトの参照を渡す前に少し説明します。すべてのパラメータは「値渡し」です。コピーが作成され、呼び出された関数に渡されます。これが興味深いのは、パラメーターが値ではなく、代わりに値を指している場合です (ただし、参照の方が正確です)。
これにより、 が得られます。. .
オブジェクト
ここで行っているのは、 Objectへの参照を渡すことです。
(@Servyが指摘したように、「値による参照の受け渡し」
。)
プリミティブ(参照型ではない)を渡した場合、 Helper の変更は helper にとどまります。. .
プリミティブ プリミティブ
を参照型のように動作させるには (これは、求めているものとは正反対です)、ref キーワードを使用する必要があります。
「オブジェクト」例外
一部の言語では、不変オブジェクトを明示的に定義できます。 つまり、不変性を実現するためのコンパイラの直接サポートがあります。例については、リンクされた記事を参照してください。
つまり、Person を作成してそのインスタンスを新しいメソッドに渡すと、そのメソッドが影響します。person の独自のコピーを受け取ります。
興味深いことに、.Net には単一のネイティブの不変型、String があります。String は Object から継承されますが、文字列を変更すると、文字列の新しいインスタンスが作成されます。パラメータがオブジェクトの別のインスタンスを指すようになりました。
明確にするために、パターンを介して他の不変オブジェクトがありますが、直接サポートされているということは、書く必要がないことを意味します
public void Run()
{
String foo = "foo";
Helper(foo);
}
public void Helper(String input)
{
input = new String(Foo + "Bar");
//Or CopyTo as seen in Arrays, etc.
}
しかし、それはできますか?- はい!!!
方法が常にある!
すべてのプロパティを読み取り専用にし、(コンストラクターを介して) インスタンス化時にのみ値を設定できるようにします。値を変更したいですか?新しいインスタンスを作成する必要があります!
この戦略を使用する場合、オブジェクトがiCloneableを実装すると役立ちます。なぜ iCloneable なのか?ディープコピーとシャローコピーあなたが外部ライブラリを書いていて、あなたのライブラリの消費者にあなたのオブジェクトを不変であると見てもらいたいと仮定すると. #1 と同じことを行いますが、private よりも許容度の高いアクセス修飾子を使用します。例:内部
それが役立つことを願っています!
更新: @Dzyannは最初に同様の回答を提供しました(非常に冗長であるため、私にぴったりです)。私の答えが気に入ったら、彼にもチェックを入れてください。
C# にないものを呼び出すのはやめるべきだと思います。ref
MSDN でおよびout
キーワードについて読むことができます。プリミティブ型であることをお知らせしました
void MyVoid(int p1)
値渡し - コピーされることを意味します。ここで修正を行う必要があります。他の型も byVal で渡されましたが、既存のオブジェクトへの参照として渡されました。
すべてのパラメーターを参照で渡すと、コードは高速になりますが、より危険になります。これは、コピーしないことで時間を節約できるためです。ただし、その場合も、高度なコーディング規律が必要になります。
「なぜ .NET が入力のみのパラメーターを持つことを許可しないのかについての合理的な説明はありません」 - これはまさに、渡されたプリミティブ型に対して .Net が効果的に持っているものbyval
です。
パラメータとして渡されたすべてのオブジェクト構造をコピーする必要がある場合、.net がどれほど非効率になるか想像してみてください。これが、MSがオブジェクトタイプを参照として渡すことにした理由だと思います