7

にはC#、次のシグネチャを持つメソッドがあります。

List<T> Load<T>(Repository<T> repository) 

メソッド内Load()で、ジェネリック型を含む完全なメソッド名を (デバッグ目的で) ダンプしたいと思います。例: 呼び出すLoad<SomeRepository>();と書き込みます"Load<SomeRepository>"

私がこれまでに試したこと: と を使用MethodBase.GetCurrentMethod()GetGenericArguments()て情報を取得します。

List<T> Load<T>(Repository<T> repository) 
{
   Debug.WriteLine(GetMethodName(MethodBase.GetCurrentMethod()));
}

string GetMethodName(MethodBase method)
{
     Type[] arguments = method.GetGenericArguments();
     if (arguments.Length > 0)
        return string.Format("{0}<{1}>", 
          method.Name, string.Join(", ", arguments.Select(x => x.Name)));
     else
        return method.Name;
}

メソッド名の取得は機能しますが、ジェネリック パラメーターの場合は常に me が返されます"T"。メソッドはLoad<T>代わりに戻りますLoad<SomeRepository>(これは役に立ちません)

GetGenericArguments()外部に呼び出しGetMethodName()て引数として提供しようとしましたが、役に立ちません。

typeof(T)のパラメーターとして提供できますがGetMethodName()(動作します)、ジェネリック型の数に固有になります。たとえばLoad<T, U>、他の引数を指定しない限り、それは機能しなくなります。

4

4 に答える 4

1

ジェネリック メソッドの名前とパラメーターを取得するためのジェネリック ソリューションが必要な場合は、次のコード サンプルのように式ツリーを使用してみてください。

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

class Program
{
    static void Main()
    {
        Load(new Repository<int>());
        Load(new Repository<string>());
        Console.ReadLine();
    }

    class Repository<T> { }

    static List<T> Load<T>(Repository<T> repository)
    {
        Dump(() => Load(repository));

        return default(List<T>);
    }

    static void Dump(Expression<Action> action)
    {
        var methodExpr = action.Body as MethodCallExpression;

        if (methodExpr == null)
            throw new ArgumentException();

        var methodInfo = methodExpr.Method;

        Console.WriteLine(methodInfo);
    }
}

出力:

ここに画像の説明を入力

于 2013-10-01T13:55:13.883 に答える
1

Jeppe Stig Nielsenの答えは、要件に関して正しいです。実際、ソリューションはTを返し、彼はランタイム型名を返します。何か違うことを求める場合は、質問を書き直してみてください。以下は、1 つの一般的なアイテムの別のソリューションです。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        Load(new Repository<int>());
        Load(new Repository<string>());
        Console.ReadLine();
    }

    class Repository<T> { }

    static List<T> Load<T>(Repository<T> repository)
    {
        Console.WriteLine("Debug: List<{1}> Load<{1}>({0}<{1}> repository)", typeof(Repository<T>).Name, typeof(Repository<T>).GenericTypeArguments.First());
        return default(List<T>);
    }
}

要求した出力は次のとおりです。

ここに画像の説明を入力

于 2013-10-01T12:46:25.223 に答える
0

リフレクションのほかに IL を使用する、あなたの質問に対する重い回答を見つけました。アイデアは、ダンプしたい子メソッドを呼び出す親メソッドの本体を取得することです。リフレクションから、IL バイトの配列を取得できます。これを読み取って、汎用パラメーターのランタイム値とともに適切なメソッド呼び出しに戻すことができます。

以下は、サンプルに基づく簡略化された結果コードです。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

class Program
{
    static void Main()
    {
        Load(new Respository<int>());
        Load(new Respository<string>());

        Console.ReadLine();
    }

    class Respository<T> { }

    static List<T> Load<T>(Respository<T> repository)
    {
        Dump(); // <-- Just dump this

        return default(List<T>);
    }

    static void Dump()
    {
        // Get the method that invoked the method being dumped
        var callerFrame = new StackFrame(2);
        var callerMethod = callerFrame.GetMethod();

        // Get the method that is being dumped
        var calleeFrame = new StackFrame(1);
        var calleeMethod = calleeFrame.GetMethod();

        // Should return one value
        var callees = from il in new ILReader(callerMethod).OfType<InlineMethodInstruction>()
                      let callee = callerMethod.Module.ResolveMember(il.Token)
                      where callee.Name == calleeMethod.Name && il.Offset == callerFrame.GetILOffset()
                      select callee;

        Console.WriteLine(callees.First());
    }
}

知らせ:

  1. Dump()には引数は必要ありません。
  2. ILReaderは、 Haibo LuoがウェブブログのRead IL from MethodBodyというタイトルの記事で作成したクラスの完成版です。

以下は、Luo のクラスとサテライト オブジェクトを簡単に完成させたものです。

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

abstract class ILInstruction
{
}

class SimpleInstruction : ILInstruction
{
    public string Name { get; private set; }

    public SimpleInstruction(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        return GetType().Name + " " + Name;
    }
}

abstract class MethodBaseInstruction : ILInstruction
{
    public MethodBase Method { get; private set; }

    public MethodBaseInstruction(MethodBase method)
    {
        Method = method;
    }

    public override string ToString()
    {
        return GetType().Name + " " + Method.Name;
    }
}

class InlineNoneInstruction : MethodBaseInstruction
{
    public int Offset { get; private set; }
    public OpCode OpCode { get; private set; }

    public InlineNoneInstruction(MethodBase method, int offset, OpCode opCode)
        : base(method)
    {
        Offset = offset;
        OpCode = opCode;
    }

    public override string ToString()
    {
        return base.ToString() + " " + Offset + " " + OpCode;
    }
}

class ShortInlineBrTargetInstruction : InlineNoneInstruction
{
    public sbyte ShortDelta { get; private set; }

    public ShortInlineBrTargetInstruction(MethodBase method, int offset, OpCode opCode, sbyte shortDelta)
        : base(method, offset, opCode)
    {
        ShortDelta = shortDelta;
    }

    public override string ToString()
    {
        return base.ToString() + " " + ShortDelta;
    }
}

class InlineMethodInstruction : InlineNoneInstruction
{
    public int Token { get; private set; }

    public InlineMethodInstruction(MethodBase method, int offset, OpCode opCode, int token)
        : base(method, offset, opCode)
    {
        Token = token;
    }

    public override string ToString()
    {
        return base.ToString() + " " + Token;
    }
}

class InlineSwitchInstruction : InlineNoneInstruction
{
    public int[] Deltas { get; private set; }

    public InlineSwitchInstruction(MethodBase method, int offset, OpCode opCode, int[] deltas)
        : base(method, offset, opCode)
    {
        Deltas = deltas;
    }

    public override string ToString()
    {
        return base.ToString() + " " + string.Join(", ", Deltas);
    }
}

class ILReader : IEnumerable<ILInstruction>
{
    Byte[] m_byteArray;
    Int32 m_position;
    MethodBase m_enclosingMethod;

    static OpCode[] s_OneByteOpCodes = new OpCode[0x100];
    static OpCode[] s_TwoByteOpCodes = new OpCode[0x100];

    static ILReader()
    {
        foreach (FieldInfo fi in typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
        {
            OpCode opCode = (OpCode)fi.GetValue(null);
            UInt16 value = (UInt16)opCode.Value;
            if (value < 0x100)
                s_OneByteOpCodes[value] = opCode;
            else if ((value & 0xff00) == 0xfe00)
                s_TwoByteOpCodes[value & 0xff] = opCode;
        }
    }

    public ILReader(MethodBase enclosingMethod)
    {
        this.m_enclosingMethod = enclosingMethod;
        MethodBody methodBody = m_enclosingMethod.GetMethodBody();
        this.m_byteArray = (methodBody == null) ? new Byte[0] : methodBody.GetILAsByteArray();
        this.m_position = 0;
    }

    public IEnumerator<ILInstruction> GetEnumerator()
    {
        while (m_position < m_byteArray.Length)
            yield return Next();

        m_position = 0;
        yield break;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return this.GetEnumerator(); }

    ILInstruction Next()
    {
        Int32 offset = m_position;
        OpCode opCode = OpCodes.Nop;
        Int32 token = 0;

        // read first 1 or 2 bytes as opCode
        Byte code = ReadByte();
        if (code != 0xFE)
            opCode = s_OneByteOpCodes[code];
        else
        {
            code = ReadByte();
            opCode = s_TwoByteOpCodes[code];
        }

        switch (opCode.OperandType)
        {
            case OperandType.InlineNone:
                return new InlineNoneInstruction(m_enclosingMethod, offset, opCode);

            case OperandType.ShortInlineBrTarget:
                SByte shortDelta = ReadSByte();
                return new ShortInlineBrTargetInstruction(m_enclosingMethod, offset, opCode, shortDelta);

            case OperandType.InlineBrTarget: Int32 delta = ReadInt32(); return new SimpleInstruction(delta.ToString());
            case OperandType.ShortInlineI: Byte int8 = ReadByte(); return new SimpleInstruction(int8.ToString());
            case OperandType.InlineI: Int32 int32 = ReadInt32(); return new SimpleInstruction(int32.ToString());
            case OperandType.InlineI8: Int64 int64 = ReadInt64(); return new SimpleInstruction(int64.ToString());
            case OperandType.ShortInlineR: Single float32 = ReadSingle(); return new SimpleInstruction(float32.ToString());
            case OperandType.InlineR: Double float64 = ReadDouble(); return new SimpleInstruction(float64.ToString());
            case OperandType.ShortInlineVar: Byte index8 = ReadByte(); return new SimpleInstruction(index8.ToString());
            case OperandType.InlineVar: UInt16 index16 = ReadUInt16(); return new SimpleInstruction(index16.ToString());
            case OperandType.InlineString: token = ReadInt32(); return new SimpleInstruction("InlineString" + token.ToString());
            case OperandType.InlineSig: token = ReadInt32(); return new SimpleInstruction("InlineSig" + token.ToString());
            case OperandType.InlineField: token = ReadInt32(); return new SimpleInstruction("InlineField" + token.ToString());
            case OperandType.InlineType: token = ReadInt32(); return new SimpleInstruction("InlineType" + token.ToString());
            case OperandType.InlineTok: token = ReadInt32(); return new SimpleInstruction("InlineTok" + token.ToString());

            case OperandType.InlineMethod:
                token = ReadInt32();
                return new InlineMethodInstruction(m_enclosingMethod, offset, opCode, token);

            case OperandType.InlineSwitch:
                Int32 cases = ReadInt32();
                Int32[] deltas = new Int32[cases];
                for (Int32 i = 0; i < cases; i++) deltas[i] = ReadInt32();
                return new InlineSwitchInstruction(m_enclosingMethod, offset, opCode, deltas);

            default:
                throw new BadImageFormatException("unexpected OperandType " + opCode.OperandType);
        }
    }

    Byte ReadByte() { return (Byte)m_byteArray[m_position++]; }
    SByte ReadSByte() { return (SByte)ReadByte(); }

    UInt16 ReadUInt16() { m_position += 2; return BitConverter.ToUInt16(m_byteArray, m_position - 2); }
    UInt32 ReadUInt32() { m_position += 4; return BitConverter.ToUInt32(m_byteArray, m_position - 4); }
    UInt64 ReadUInt64() { m_position += 8; return BitConverter.ToUInt64(m_byteArray, m_position - 8); }

    Int32 ReadInt32() { m_position += 4; return BitConverter.ToInt32(m_byteArray, m_position - 4); }
    Int64 ReadInt64() { m_position += 8; return BitConverter.ToInt64(m_byteArray, m_position - 8); }

    Single ReadSingle() { m_position += 4; return BitConverter.ToSingle(m_byteArray, m_position - 4); }
    Double ReadDouble() { m_position += 8; return BitConverter.ToDouble(m_byteArray, m_position - 8); }
}
于 2013-10-02T10:27:13.190 に答える
-1

使用できるようです:

List<T> Load<T>(Repository<T> repository) 
{
  Debug.WriteLine(
    ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(T)).ToString()
    );
}

このToString()コンテキストでは、おそらく省略できます。

GetCurrentMethod「定義」を与えるようです。このように構築されたジェネリック メソッドを「作成」する必要があります。

この「解決策」には、 のジェネリック シグネチャがegに変更され、 の本体の呼び出しが更新されていない場合、コンパイルは問題なく実行されますが、実行時に爆発するという問題がまだあります。Load<T>(...)Load<TRep, TOther>(...)MakeGenericMethodLoad<,>

アップデート:

よりシンプルでより良い解決策を見つけました:

public static MethodBase GetCurrentMethod()
{
  var sf = new StackFrame(1);
  return sf.GetMethod();
}

ジェネリック メソッドの短いスレッド スタック トレースがあります- 実行時の T は何でしたか? MSDN では、簡単な解決策は存在しないと主張されています。SO のこちらのスタック内のクラスから汎用引数を取得するも参照してください。

于 2013-10-01T09:59:23.120 に答える