.NET リフレクターが .NET Framework で行われたことを示しているのに、プロパティまたはインデクサーを ref パラメーターとして渡すことができないのはなぜですか?
-
05-07-2019 - |
質問
さて、やろうとしていることを示すために .NET リフレクターから切り取って貼り付けます。
public override void UpdateUser(MembershipUser user)
{
//A bunch of irrelevant code...
SecUtility.CheckParameter(ref user.UserName, true, true, true, 0x100, "UserName");
//More irrelevant code...
}
このコード行は、.NET Framework の System.Web.Security.SqlMembershipProvider.UpdateUser (System.Web.dll v2.0.50727) から直接取得されます。
SecUtility.CheckParameter には、最初のパラメータとして参照値が必要です。これには、引数として渡されたユーザーのプロパティが渡されます。
CheckParameter コードの定義は次のとおりです。
internal static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas, int maxSize, string paramName)
{
//Code omitted for brevity
}
やっていることはすべて理にかなっています - 机上では...そこで、似たようなものを使用したい場所で、簡単な小さなプロトタイプを作成しました。
public class DummyClass
{
public string ClassName{ get; set; }
}
public class Program
{
private static DoSomething(ref string value)
{
//Do something with the value passed in
}
public static Main(string[] args)
{
DummyClass x = new DummyClass() { ClassName = "Hello World" };
DoSomething(ref x.ClassName); //This line has a red squiggly underline
//under x.ClassName indicating the
//error provided below.
}
}
このコードはコンパイルできません。次のようなエラーが表示されます。
"A property or indexer may not be passed as an out or ref parameter"
けっこうだ...しかし、なぜ私のコードでは .NET Framework コード ベースにあると思われることを実行できないのでしょうか?これは .NET Reflector が DLL を解釈する方法に問題があるのでしょうか、それともコードを解釈する方法に問題があるのでしょうか?
解決
それはReflectorからのいくつかの悪い解釈だと思います。実際、次のようにコードを記述した場合:
static void Main(string[] args)
{
DummyClass x = new DummyClass();
string username = x.ClassName;
DoSomething(ref username);
}
リリースモードでコンパイルすると、Reflectorで次のように表示されます。
static void Main(string[] args)
{
DummyClass x = new DummyClass();
DoSomething(ref x.ClassName);
}
C#コンパイラはC#コードではなくILを生成しているため、Reflectorで表示されるものが常に現実とは限らないことに注意してください。そのため、内部で何が起こっているかを明確に理解するために、コンパイラーによって生成された実際のコードを見ることができます。
L_000f: callvirt instance string System.Web.Security.MembershipUser::get_UserName()
L_0014: stloc.0
L_0015: ldloca.s str
L_0017: ldc.i4.1
L_0018: ldc.i4.1
L_0019: ldc.i4.1
L_001a: ldc.i4 0x100
L_001f: ldstr "UserName"
L_0024: call void System.Web.Util.SecUtility::CheckParameter(string&, bool, bool, bool, int32, string)
ローカル変数が使用されていることは明らかです。
他のヒント
リフレクターのバグです。実際には参照によってプロパティを渡しているわけではありません。
これを再現する C# コードを次に示します。
using System;
class Person
{
public string Name { get; set; }
}
class Test
{
static void Main(){} // Just make it easier to compile
static void Foo(Person p)
{
string tmp = p.Name;
Bar(ref tmp);
}
static void Bar(ref string x)
{
}
}
リフレクターはこのコードを示します Foo
:
private static void Foo(Person p)
{
Bar(ref p.Name);
}
これは無効な C# であるだけでなく、誤解を招くものでもあります。 x
内で Bar
何らかの形で変更するだろう p.Name
- 元の C# コードを見るとそうではありません。
元のサンプルでは、さらに意味が薄れます。 UserName
は読み取り専用プロパティです!
関数に渡す前に、プロパティの値を変数に設定してみてください。
string myClassName = x.ClassName
DoSomething(ref myClassName);
これは最もエレガントな解決策ではありませんが、正しい方向を示しているはずです。上記のコメントでYuriyが言ったように、おそらくプロパティのgetおよびsetを明示的に宣言していないという事実と関係があります。
変数をrefまたはoutとして渡すと、呼び出しは実際に元の場所にあるメモリを指します。そのため、プロパティを参照として渡すことが許可されている場合、クラスメンバの一貫性が失われていることを意味します。これがこの問題の背後にある理由です。