C # передать любой метод в качестве параметра - PullRequest
6 голосов
/ 26 марта 2011

При ведении журнала вы всегда запутываетесь в строковых литералах.

Я решил это красиво для свойств, полей и переменных, передав Expression<Func<T>> expression (как объяснено здесь ), так что выможет делать что-то вроде этого:

public void Demo(string someArgument)
{
    LogFrameWork.LogLine("Demo"); // goal is to get rid of these string literals
    LogFramework.Log(() => someArgument);
}

Я хочу сделать что-то подобное для самого метода Demo:

public void Demo(string someArgument)
{
    LogFramework.Log(this.Demo);
}

Я пробовал такие вещи:

public static void Log(Delegate method)
{
    string methodName = method.Method.Name;
    LogLine(methodName);
}

и это:

public static void Log(Action method)
{
    string methodName = method.Method.Name;
    LogLine(methodName);
}

Но я получаю ошибки компилятора, подобные этим:

Argument 1: cannot convert from 'method group' to 'System.Delegate' 
Argument 1: cannot convert from 'method group' to 'System.Action'   

Я мог бы ввести кучу перегрузок, используя Func <...> и Действие <...> , но это звучит слишком сложно.

Есть ли способ покрыть это для любого метода с любым количеством параметров и дополнительным результатом?

- jeroen

PS: я думаю этот вопрос может иметь какое-то отношение здесь, но нет ответов, которые вызвали бы у меня чувство "ага": -)

Ответы [ 5 ]

5 голосов
/ 26 марта 2011

Это намного сложнее, чем кажется. Я думаю, что вам лучше всего использовать универсальные перегрузки Func и Action, но есть способ сделать это с деревьями выражений. Вот пример в LINQPad:

public static void Log(Expression<Action> expr)
{
    Console.WriteLine(((MethodCallExpression)expr.Body).Method.Name);
}

void Main()
{
    Log(() => DoIt());
    Log(() => DoIt2(null));
    Log(() => DoIt3());
}

public void DoIt()
{
    Console.WriteLine ("Do It!");
}

public void DoIt2(string s)
{
    Console.WriteLine ("Do It 2!" + s);
}

public int DoIt3()
{
    Console.WriteLine ("Do It 3!");
    return 3;
}

Это выводит:

DoIt
DoIt2
DoIt3

Обратите внимание, что мне пришлось использовать лямбда-выражения и указать фиктивные аргументы при вызове метода Log.

Это основано на Отличный ответ Федора Сойкина .

5 голосов
/ 26 марта 2011

Вы также можете достичь этого, не используя ExpressionTrees через System.Diagnostics.StackTrace.

StackTrace trace = new StackTrace();

И затем:

trace.GetFrame(0).GetMethod().Name

Чтобы получить MethodInfo, а затем имя текущего метода или:

trace.GetFrame(1).GetMethod().Name 

Чтобы получить вызывающий метод.

3 голосов
/ 26 марта 2011

Вместо того, чтобы пытаться передать метод в качестве параметра вашему регистратору, посмотрите на него с точки зрения того, чтобы регистратор идентифицировал вызывающий метод.

Вот (псевдо) пример:

Класс регистратора

public void Debug( string message )
{
  message = string.Format( "{0}: {1}", GetCallingMethodInfo(), message );
  // logging stuff
}

/// <summary>
/// Gets the application name and method that called the logger.
/// </summary>
/// <returns></returns>
private static string GetCallingMethodInfo()
{
  // we should be looking at the stack 2 frames in the past:
  // 1. for the calling method in this class
  // 2. for the calling method that called the method in this class
  MethodBase method = new StackFrame( 2 ).GetMethod();
  string name = method.Name;
  string type = method.DeclaringType.Name;

  return string.Format( "{0}.{1}", type, name );
}

Везде, где используется регистратор:

// resides in class Foo
public void SomeMethod()
{
  logger.Debug("Start");
}

Выходные данные из регистратора будут: Foo.SomeMethod: Start

0 голосов
/ 26 марта 2011

Попробуйте:

/// <summary>
/// Trace data event handler delegate.
/// </summary>
/// <returns>The data to write to the trace listeners</returns>
public delegate object TraceDataEventHandler();

public static class Tracing
{

    /// Trace a verbose message using an undefined event identifier and message.
    /// </summary>
    /// <param name="message">The delegate to call for the trace message if this event should be traced.</param>
    [Conditional("TRACE")]
    public static void TraceVerbose(TraceMessageEventHandler message)
    {
        ... your logic here
    }
}

Тогда вы можете сделать ...

Tracing.TraceVerbose(() => String.Format(...));

Надеюсь, я правильно понял ваш вопрос ... это делает то, что вы хотите?

0 голосов
/ 26 марта 2011

Вы можете определить делегата, а затем принять его в качестве параметра.

public delegate void DemoDelegate(string arg);

public void MyMethod(DemoDelegate delegate)
{
    // Call the delegate
    delegate("some string");
}

Вы можете вызвать MyMethod следующим образом:

MyMethod(delegate(string arg) 
{
   // do something
});

или

void MethodThatTakesAString(string value)
{
    // do something
}

MyMethod(MethodThatTakesAString);

См. Эту ссылку для получения дополнительной информации:

http://msdn.microsoft.com/en-us/library/aa288459(v=vs.71).aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...