問題は、そのようなメソッドを作成することではありません。それはメソッドを呼び出すことです。if(this == null)
コードにのテストを入れると、それは完全に有効です。ヒットすることが「不可能」であるという理由で、コンパイラーによって最適化できると思いますが、ありがたいことにそうではありません。
ただし、メソッドを呼び出すと、を介して実行されるcallvirt
ため、メソッドを直接呼び出すのではなく、仮想メソッドの場合と同じように、特定のインスタンスを呼び出すメソッドのバージョンが検出されます。これはnull参照では失敗するため、完全に優れたセルフnullテストメソッドは、呼び出される前に失敗します。
C#は意図的にこれを行います。エリック・ガンナーソンによれば、これはあなたにそうさせるのは少し奇妙だと彼らが思ったからです。
C++をモデルにした.NET言語に.NETと同じ会社が作成したC++コンパイラ*の両方で完全に許容できることをさせることがなぜ少し奇妙だと考えられたのか理解できませんでした。許可されなかったのは少し変だといつも思っていました。
クラスを呼び出す別の言語(F#またはIL)から何かを追加するか、それを使用Reflection.Emit
して正常に機能するデリゲートを生成するために使用できます。たとえば、次のコードは、でGetHashCode
定義されたバージョンを呼び出しますobject
(つまり、GetHashCode
オーバーライドされた場合でも、オーバーライドは呼び出されません)。これは、nullインスタンスで安全に呼び出すことができるメソッドの例です。
DynamicMethod dynM = new DynamicMethod(string.Empty, typeof(int), new Type[]{typeof(object)}, typeof(object));
ILGenerator ilGen = dynM.GetILGenerator(7);
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, typeof(object).GetMethod("GetHashCode"));
ilGen.Emit(OpCodes.Ret);
Func<object, int> RootHashCode = (Func<object, int>)dynM.CreateDelegate(typeof(Func<object, int>));
Console.WriteLine(RootHashCode(null));
これの良い点の1つは、保持できるRootHashCode
ため、1回だけビルドする必要があり(静的コンストラクターなど)、繰り返し使用できることです。
もちろん、これは他のコードにnull参照を介してメソッドを呼び出させることには価値がありません。あなたが提案するような拡張メソッドが唯一の賭けだからです。
もちろん、このC#の癖がない言語で書いている場合は、C#の人々ができるので、null参照を呼び出すための「デフォルト」の結果を取得するための代替手段を提供する必要があることにも注意してください。取得します。C#と同じように、一部の言語では大文字と小文字を区別できないため、パブリック名の大文字と小文字のみの違いは避けてください。
編集:あなたの質問IsAuthorized
が呼ばれている完全な例。投票は、それができると信じていない人がいることを示唆しているからです(!)
using System;
using System.Reflection.Emit;
using System.Security;
/*We need to either have User allow partially-trusted
callers, or we need to have Program be fully-trusted.
The former is the quicker to do, though the latter is
more likely to be what one would want for real*/
[assembly:AllowPartiallyTrustedCallers]
namespace AllowCallsOnNull
{
public class User
{
public bool IsAuthorized
{
get
{
//Perverse because someone writing in C# should be expected to be friendly to
//C#! This though doesn't apply to someone writing in another language who may
//not know C# has difficulties calling this.
//Still, don't do this:
if(this == null)
{
Console.Error.WriteLine("I don't exist!");
return false;
}
/*Real code to work out if the user is authorised
would go here. We're just going to return true
to demonstrate the point*/
Console.Error.WriteLine("I'm a real boy! I mean, user!");
return true;
}
}
}
class Program
{
public static void Main(string[] args)
{
//Set-up the helper that calls IsAuthorized on a
//User, that may be null.
DynamicMethod dynM = new DynamicMethod(string.Empty, typeof(bool), new Type[]{typeof(User)}, typeof(object));
ILGenerator ilGen = dynM.GetILGenerator(7);
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, typeof(User).GetProperty("IsAuthorized").GetGetMethod());
ilGen.Emit(OpCodes.Ret);
Func<User, bool> CheckAuthorized = (Func<User, bool>)dynM.CreateDelegate(typeof(Func<User, bool>));
//Now call it, first on null, then on an object
Console.WriteLine(CheckAuthorized(null)); //false
Console.WriteLine(CheckAuthorized(new User()));//true
//Wait for input so the user will actually see this.
Console.ReadKey(true);
}
}
}
ああ、そしてこれにおける現実の実際的な懸念。C#の動作の良い点は、null参照の呼び出しでフェイルファストが発生し、途中でフィールドまたは仮想にアクセスするために失敗することです。これは、呼び出しを作成するときにnullインスタンスにあるかどうかを心配する必要がないことを意味します。ただし、完全にパブリックなメソッド(つまり、パブリッククラスのパブリックメソッド)で防弾を使用したい場合は、これに依存することはできません。メソッドのステップ1の後に常にステップ2が続くことが重要であり、ステップ2がnullインスタンスで呼び出された場合にのみ失敗する場合は、自己nullチェックが必要です。これが発生することはめったにありませんが、C#以外のユーザーには、上記の手法を使用しないとC#で再現できないバグが発生する可能性があります。
*ただし、これはコンパイラに固有です。C++標準IIRCでは定義されていません。