つたないですがTestCodeを
とても意外な結果でした。
指摘があったのですが、これを参照値渡し
と呼び、参照渡しとは区別されるそうです。
まあ普通に便利だったんでいいんですが。
XAML
<Window x:Class="DataClassの参照渡し.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataClassの参照渡し"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Button x:Name="button1" Height="50" Width="100"
HorizontalAlignment="Left" Content="button1" Click="button1_Click"/>
<Button x:Name="button2" Height="50" Width="100"
HorizontalAlignment="Left" Content="button2" Click="button2_Click"/>
<Label x:Name="label1" FontSize="16"/>
</StackPanel>
</Grid>
</Window>
Code Behaind
using System.Windows;
namespace DataClassの参照渡し
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
button1DataClass bu1d = null!;
private void button1_Click(object sender, RoutedEventArgs e)
{
//Buttonを押すとisSucessffmpegがtrue
bu1d = new();
bu1d.isSucessffmpeg = true;
label1.Content = $"{nameof(bu1d.isSucessffmpeg)} は $\"{bu1d.isSucessffmpeg}\"";
//結果
//sSucessffmpeg ( $"True"
}
private void button2_Click(object sender, RoutedEventArgs e)
{
//Buttonを押すとbutton1DataClassを参照渡しし、button2DataClassのisSucessffmpegがfalse
button2DataClass bu2d = new(bu1d);
bu2d.bu1dinBud1Class.isSucessffmpeg = false;
label1.Content = $"{nameof(bu1d.isSucessffmpeg)} は $\"{bu1d.isSucessffmpeg}\"";
//結果 isSucessffmpeg は $"False"
}
public class button1DataClass
{
public bool isSucessffmpeg;
}
public class button2DataClass
{
public button2DataClass(button1DataClass bu1d)
{
bu1dinBud1Class = bu1d;
//データクラスを参照渡し
}
public button1DataClass bu1dinBud1Class;
}
}
}
普通こうなると思わないじゃないですか?
Falseにならなかったらstaticで渡すつもりだったんで想定外の挙動でした(笑)
ChatGPTが同じことを言うので、ハルシネーションだと思っていました。
更に言うと、普通こんなことしないでメソッドで引数取ると思う(笑) 別に完全コンストラクタじゃないし
ChatGPTによると参照型の基本的な仕様だそうで、C#1.0から変わってないそうです。あんま気づきにくい事だと思う
これは参照渡し(ref)ではなく参照値渡しである
という指摘があった。
そこでChatGPTで何が出来て、何が出来ないのか聞いてみた。
参照渡し(ref
)でできて、参照値渡し(通常の参照型オブジェクトの渡し方)ではできないことをいくつか具体的に説明します。
1. 呼び出し元の変数自体を変更する
参照渡し(ref
)では、呼び出し元の変数そのものを関数やメソッド内で変更することができます。これに対し、参照値渡し(通常の参照型オブジェクトの渡し方)では、変数の中身(オブジェクト自体)のプロパティなどを変更することはできますが、変数そのものを別のオブジェクトに置き換えることはできません。
例: 参照渡し (ref
) の場合
public class DataClass
{
public bool isSuccess;
}
public void ModifyObject(ref DataClass obj)
{
// 新しいオブジェクトを生成して、元のオブジェクトを置き換える
obj = new DataClass();
obj.isSuccess = true;
}
DataClass myObj = new DataClass();
ModifyObject(ref myObj);
// myObjは新しいオブジェクトに置き換えられ、isSuccessはtrueに設定される
この例では、ref
を使っているため、ModifyObject
メソッド内でmyObj
変数自体を新しいインスタンスに置き換えることができます。呼び出し元のmyObj
は、新しいオブジェクトを指すようになります。
例: 通常の参照値渡しの場合
public void ModifyObject(DataClass obj)
{
// これは元のオブジェクトに影響を与えない
obj = new DataClass();
obj.isSuccess = true;
}
DataClass myObj = new DataClass();
ModifyObject(myObj);
// myObjは元のオブジェクトのままで、isSuccessも変更されない
この場合、obj
に新しいインスタンスを代入しても、呼び出し元のmyObj
は変更されません。obj
はローカルな変数なので、関数のスコープが終わるとobj
への変更は消えます。
2. 値型でもオブジェクトのプロパティを変更できる
参照渡し(ref
)は、値型(structなど) を渡す場合にも使えます。この場合、関数内で値型のフィールドやプロパティを変更しても、呼び出し元に反映されます。一方、通常の値渡しでは、値型のコピーが渡されるため、関数内での変更は呼び出し元に影響を与えません。
例: 値型の参照渡し
public struct MyStruct
{
public int Value;
}
public void ModifyStruct(ref MyStruct myStruct)
{
myStruct.Value = 100;
}
MyStruct myStruct = new MyStruct();
ModifyStruct(ref myStruct);
// 呼び出し元の myStruct.Value が 100 に変更される
ref
を使うことで、値型であっても元のオブジェクトの値を変更できます。
例: 通常の値渡し
public void ModifyStruct(MyStruct myStruct)
{
myStruct.Value = 100;
}
MyStruct myStruct = new MyStruct();
ModifyStruct(myStruct);
// myStruct.Value は変更されない
この場合、myStruct
はコピーされて渡されるため、関数内での変更は呼び出し元に影響を与えません。
3. 複数のout
やref
引数を使って値を返す
ref
やout
キーワードを使うと、メソッドが複数の値を返すことが可能です。通常の参照値渡しでは、1つのオブジェクトのプロパティなどを変更することはできますが、複数の変数に対して一度に値を返すことはできません。
例: 複数のref
引数
public void ModifyValues(ref int a, ref int b)
{
a += 10;
b += 20;
}
int x = 5;
int y = 15;
ModifyValues(ref x, ref y);
// x は 15, y は 35 に変更される
この場合、2つのref
引数を使って、2つの異なる変数の値を一度に変更することができます。
まとめ
-
呼び出し元の変数自体を変更できる:
ref
を使うと、呼び出し元の変数自体を新しいオブジェクトで置き換えることが可能。通常の参照値渡しでは、このような動作はできません。 -
値型に対しても影響を与えられる:
ref
を使えば、値型(struct
など)でも呼び出し元に変更を反映させることが可能。 -
複数の変数に対して一度に値を返す:
ref
やout
を使えば、複数の変数に同時に値を返すことができるが、通常の参照値渡しではこれは不可能です。
これらの特徴により、ref
を使うことで、通常の参照値渡しではできない強力な操作が可能になります。
あとがき
通常の参照値渡しでも十分だと思うけど(今回の場合はアドレスのコピーで十分)、まあrefの方がいろいろ出来るのは分った。