2016-03-23 5 views
3

私はさまざまなシナリオを説明するためのサンプルとして使用することを、次のコードを持っている:オプションのGuidのリフレクションでDefaultValueを取得していますか?

public static void MethodWithOptionalGuid(Guid id = default(Guid)) { } 

    public static void MethodWithOptionalInteger(int id = 2) { } 

    public static void MethodWithOptionalString(string id = "33344aaa") { } 

    public static void MethodWithoutOptionalParameter(int id, Guid longId) { } 

    static void Main(string[] args) 
    { 
     var methods = typeof(Program).GetMethods(BindingFlags.Public | BindingFlags.Static).ToList(); 

     foreach (var method in methods) 
     { 
      PrintMethodDetails(method); 
     } 

     Console.ReadLine(); 
    } 

    static void PrintMethodDetails(MethodInfo method) 
    { 
     Console.WriteLine(method.Name); 

     foreach (var parameter in method.GetParameters().ToList()) 
     { 
      Console.WriteLine(parameter.Name + 
           " of type " + parameter.ParameterType.ToString() + 
           " with default value:" + parameter.DefaultValue); 
     } 

     Console.WriteLine(); 
    } 

それは次のように出力されます

MethodWithOptionalGuid 
id of type System.Guid with default value: 

MethodWithOptionalInteger 
id of type System.Int32 with default value:2 

MethodWithOptionalString 
id of type System.String with default value:33344aaa 

MethodWithoutOptionalParameter 
id of type System.Int32 with default value: 
longId of type System.Guid with default value: 

出力は、最後の3つの方法のための罰金です。

私の質問は、最初のMethodWithOptionalGuidに関するものです:Guidのデフォルト値が認識されないのはなぜですか?

"0000000 -..."のようなものを受け取ると予想されます。私はまた、新しいGuid()と同じ結果でオプションのパラメータを初期化しようとしました。私はTimeSpanのような他の構造体も試してみたところ、動作は同じです。

私はすべての値の型が(整数の例に見られるように)同じように動作すると予想しました。

追加:Asp.Net MVCでオプションのGuidパラメータを使用してアクションを実行しようとしているときにこの問題が検出されましたが、失敗しました(Guidをnullにする必要がありました)。 MVCコードを調べ、ある時点でDefaultValueを使用していることがわかりました。だから私は私の問題をよりよく説明するために、このコードサンプルを作成しました。

+0

を行うことができませんでした。私は最後の3つの方法について、出力が私が期待したものであることに同意しますが、私は最初の方法の出力を理解するのに問題があります。 –

+0

私はOPの質問は主に 'MethodWithOptionalGuid'だと思います。デフォルト値が指定されていて、なぜデフォルト(Guid)が "0000 .."にならないのか不思議です。 –

+0

@RaphaëlAlthausまたデフォルト(Guid)|新しいGuid()はコンパイラに既知の値であり、動作するはずです。根本原因は、Guid.Emptyを使用できない理由を説明します。 –

答えて

5

はなぜなら、このようなことをしたいときには本当に重要なことです。ドラゴンズはここに住んでいる。あなたがこの方法を見てIldasm.exeをを使用している場合、あなたはわかります

.param [1] = nullref 

ヌル構造タイプのデフォルトとして、おっと。 .paramディレクティブのセマンティクスは、CLI仕様、Ecma-335、第II.15.4.1.4章で説明されています。それは非常に短いですが、私は(読みやすくするために編集された)章全体

MethodBodyItem :: = .PARAM '[' のInt32 ']' [ '=' FieldInit]

このディレクティブストアをコピー、ペーストしますメタデータ内でメソッドパラメータ番号Int32に関連付けられた定数値 を参照してください。CLIではパラメータに値を指定する必要がありますが、この属性が存在するツールを使用すると、ユーザーではなくツールが の値をパラメータとして指定していることを示すことができます。 .paramは、CIL命令とは異なり、メソッドの戻り値を指定するためにインデックス0を使用し、 メソッドの最初のパラメータを指定するインデックス1、メソッド の2番目のパラメータを指定するインデックス2などを使用します。

[注意:CLIはこれらの値にセマンティックを全く付けません。つまり、意味のある意味(例えば、デフォルトの引数値)を実装するのは、完全にコンパイラです。終了ノート]

[注意]が最も適切です。 Reflectionを使用すると、コンパイラの役割が果たされ、デフォルト値を正しく解釈するかどうかはあなた次第です。つまり、C#では構造体タイプのデフォルト値はnullであることがわかります。

これは、[FieldInit]に格納できるものの制限です。初期化値はアセンブリのメタデータに格納されなければならず、[属性]宣言と同じ種類の制限を受けます。構造体ではなく、単純な値型を使用できます。だからコンベンションで C#のデザイナーは代わりにnullを使用してこれを回避しました。 default(Guid)が意図されていることをC#コンパイラが理解するのに十分です。

問題がどこで終わるかではなく、オプションの引数を示す別の方法がありますが、[OptionalAttribute]はC#v4.0より前に使用されていました。 COM interopライブラリやVB.NETやC++/CLIのような他の言語でも、今日でも使用されています。それがドラゴンズが生きる場所です。今のところ、これらの獣を無視

、あなたが持っているものの回避策のコードは次のようになります。

var def = parameter.DefaultValue; 
    if (parameter.ParameterType.IsValueType && def == null) { 
     def = Activator.CreateInstance(parameter.ParameterType); 
    } 
    Console.WriteLine(parameter.Name + 
         " of type " + parameter.ParameterType.ToString() + 
         " with default value:" + def); 
+0

'const'にはなれないものはすべてnullと評価されますか?だから '単純型'、 'enum型'と 'string'だけがnullに評価されないでしょうか? (文字列がref型であるので、 'default(strting)'の特別な場合を除いて、もちろんヌルです) –

+0

これはもう1つのドラゴンを呼び出します。 * const GUid *を宣言することはできませんが、デフォルト値として正常に動作することがわかります。そうではありません、あなたはそれを言うことができません。 –

1

申し訳ありません - それは正しい値を返しませんなぜ私はあなたを伝えることはできませんが、私は、この拡張メソッドを使用してworkarroundをしました:

public static object GetDefaultValue(this ParameterInfo p) 
{ 
    if (!p.Attributes.HasFlag(ParameterAttributes.HasDefault)) 
    { 
     return null; 
    } 

    if (p.DefaultValue != null || p.RawDefaultValue != null) 
    { 
     return p.DefaultValue ?? p.RawDefaultValue; 
    } 

    return p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null; 
} 

多分それは役立ちます。

+0

答えのThx。不幸にも、私はWHYにもっと興味があります。私はどのようにここに来たのかを説明する質問を更新しました。再びThx。 –

2

は、お好みの逆コンパイラを使用してテストファイルを開いて、あなたはあなたのコードが実際にコンパイルさていることがわかります。DefaultValueプロパティがnullを返す理由です

public static void MethodWithOptionalGuid(Guid id = null) { } 

Console.WriteLine(id); 

コンパイラがWriteLineメソッドの前に

box [mscorlib]System.Guid 

文を挿入します:あなたは今、このパラメータを使用する場合 、追加言うことができます。今度は出力が "0000000 -..."となるでしょう。

1

を私は

(誰かがより適切なものを見つけることが)答えはC#言語仕様で見つけることができると思います

ポイント4.1.2

式のオペランドがすべて単純型の定数である場合には、コンパイラがで式を評価するために、それ が可能です コンパイル時。そのような表現は定数式 (§7.19)として知られています。他の構造体型 で定義された演算子を含む式は、定数式とはみなされません。

const宣言を使用して、 単純型(§10.4)の定数を宣言することができます。他の 構造体型の定数を持つことはできませんが、static readonly フィールドでも同様の効果が得られます。

ここで式やconstについて話していなくても、コンパイル時に単純な型(intなどの構造体)しか評価/計算できないようです。そのため、default(int)を評価してからコードに0と表示されます。

しかし、他の構造体はコンパイル時には計算できないので、それがnullと評価された理由です。

デフォルトのパラメータ値を設定するときは、コンパイル時定数でなければなりません。

トリッキーな部分は、あなたが受け入れられ、コンパイル時定数としてdefault(Guid)またはnew Guid()を置く(ただしnullに評価される)ことができるということですが、...それはconstのすることはできません。

たとえば、あなたは私が私の質問を更新しvc74 @

const Guid g = new Guid(); 
関連する問題