8

プロジェクトをVS2010からVS2012に移行しているときに、次の問題が発生しました。プロジェクトはReflectionを頻繁に使用しており、インターフェイスからMethodInfoを取得するために、次のコードが配置されました。

Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = scv => scv.Get;

UnaryExpression unaryExpression = expression.Body as UnaryExpression;

MethodCallExpression methodCallExpression = unaryExpression.Operand as MethodCallExpression;

ConstantExpression constantExpression = methodCallExpression.Arguments[2] as ConstantExpression;

MethodInfo myMethod = constantExpression.Value as MethodInfo;

これはVS2010でコンパイルすると正常に機能しましたが、コードが.Net 4.0を目指してVS2012でコンパイルされた場合、methodCallExpression.Arguments.Count()は2でした。

逆コンパイルした後、コンパイラが同じ式に対して異なるコードを生成していることに気付きました。

これは設計上の問題です。これは、methodCallExpression.Arguments[2]の番号2のような「マジックナンバー」を設計が中継してはならないためです。私は以下を使用してこれの解決策を見つけようとしました:

MethodCallExpression outermostExpression = expression .Body as MethodCallExpression;
MethodInfo myMethod = outermostExpression.Method;

ただし、outermostExpressionはnullです。

最後に、次のように式を変更して動作させました。

Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest));
MethodCallExpression outermostExpression = expression.Body as MethodCallExpression;
Assert.AreEqual("Get", outermostExpression.Method.Name);

理想的ではありませんが、VS2010とVS2012の両方で機能します。

次のような式からMethodInfoを見つける方法はありますか?

Expression<Func<ITest, ServiceResponse>> expression = scv => scv.Get(default(ServiceRequest));
MethodInfo methodInfo = GetInnerMethodInfo( expression );
Assert.AreEqual("Get", methodInfo.Name);
4

1 に答える 1

3

式のコンパイル方法に違いがある理由がわかりません。ただし、定数デリゲートでメソッドのメソッド情報を探している場合は、ITest実装を使用して式をコンパイルし、デリゲートを取得できますMethodInfo。例えば:

Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = scv => new Func<ServiceRequest, ServiceResponse>(scv.Get);
Func<ServiceRequest, ServiceResponse> ret = expression.Compile()(new Test());
MethodInfo methodInfo = ret.Method;

..Testいくつかのクラスと実装はどこにありますかITest。これは2012年と2010年の両方で機能します。

最初にコンパイルせずに、2012年に式からそのメソッド情報を取得する方法がわかりません...

アップデート:

式のコンパイルがオプションでない場合、コンパイラは別の式を生成し、C#5コンパイラMethodInfoのプロパティにを入れているようです。MethodCallExpression.Objectそのプロパティがそうでないかどうかを確認しnull、その値をに使用するか、コレクションMethodInfo内の要素の取得を続行できます。Arguments例えば:

Expression<Func<ITest, Func<ServiceRequest, ServiceResponse>>> expression = 
    scv => new Func<ServiceRequest, ServiceResponse>(scv.Get);

UnaryExpression unaryExpression = expression.Body as UnaryExpression;

MethodCallExpression methodCallExpression = 
    unaryExpression.Operand as MethodCallExpression;

ConstantExpression constantExpression = 
    methodCallExpression.Object as ConstantExpression;

MethodInfo methodInfo;
if (constantExpression != null)
{
    methodInfo = constantExpression.Value as MethodInfo;
}
else
{
    constantExpression = methodCallExpression.Arguments
                            .Single(a => a.Type == typeof(MethodInfo) 
                                && a.NodeType == ExpressionType.Constant) as
                            ConstantExpression;
    methodInfo = constantExpression.Value as MethodInfo;
}

LINQクエリを使用してArgumentsコレクション内の要素を取得しています。ハードコードされたインデックスが必要な場合は、代わりにそれを使用できます。より完全なエラーチェックも必要です。

于 2012-08-28T21:34:04.067 に答える