Вопрос

I want to know if there is a way to define a generic function with variable number of parameters. For example, I have defined the generic function:

  public T MeasureThis<T>(Func<T> funcToMeasure)
    {
        var watch = new Stopwatch();
        watch.Start();
        T returnVal = funcToMeasure();
        watch.Stop();
        Console.WriteLine("Time elapsed: {0}", watch.Elapsed);
        return returnVal;
    }

and also:

    public T MeasureThis<T>(Func<int,int,T> funcToMeasure, int p1, int p2)
    {
        var watch = new Stopwatch();
        watch.Start();
        T returnVal = funcToMeasure(p1, p2);
        watch.Stop();
        Console.WriteLine("Time ellapsed: {0}", watch.Elapsed);
        return returnVal;
    }

I want to measure the time functions need to be executed till the end. The problem is that the functions to be measured can have none, one, two, three, .... parameters. Should I define ten times the same function if I want to measure functions that contain till 10 parameters?

Thanks!

Это было полезно?

Решение

A simple trick is to just use the most simple Func<T> which returns a type, and instead of passing the other types as type arguments to the generic, you use a closure to capture them from the surrounding context.

For example:

int SomeFunctionWith3Args(int arg1, int arg2, int arg3) { ... }

.

int[] arg = new int[] { 1, 2, 3 };
var x = MeasureThis<int>(() => SomeFunctionWith3Args(arg[0], arg[1], arg[2]));

If you're not familiar with how this kind of closure works, it basically creates a new type which houses the arguments you capture as fields, and implements the lambda as a method of the class - then replaces the call site with an instantiation of the class and a call to the method. For example, the above is (conceptually) equivalent to:

int[] arg = new int[] { 1, 2, 3 };
var closure = new TheClosure();
closure._captured = arg;
var x = MeasureThis<int>(closure.TheLambda());

where

class TheClosure {
    public int[] _captured;
    public int TheLambda() {
        return SomeFunctionWith3Args(_captured[0], _captured[1], _captured[2]);
    }
}

Другие советы

Use another generic type X to represent a class with many properties.

public T MeasureThis<T>(Func<X,T> funcToMeasure, X x) where X : class
{
    var watch = new Stopwatch();
    watch.Start();
    T returnVal = funcToMeasure(x);
    watch.Stop();
    Console.WriteLine("Time ellapsed: {0}", watch.Elapsed);
    return returnVal;
}

If you're using stock CLR delegates (e.g., Func<T,TResult> ), your methods will need to match the delegate signature. You can create a method with an optional parameter:

public int Foo( int x , int y = int.MinValue ) { ... }

and assign it to an appropriate delegate without a problem:

Func<int,int,int> delegateX = Foo ;

(But note that you won't be able to omit the second parameter when invoking it via the delegate). But you can't do this:

Func<int,int> delegateY = Foo ;

However, there's nothing to stop you from creating your own delegates. You're not required to use the standard delegates supplied by the CLR.

As more methods that take optional arguments...you can do this:

public TResult Foo<T1,T2,TResult>( T1 p0 , params T2[] p1 ) { ... }

to let you invoke the method with any number of values for the second parameter.

You can use overloads:

public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1 , T2 p2 , T2 p3 ) { ... }
public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1 , T2 p2         ) { ... }
public TResult Foo<T1,T2,TResult( T1 p0 , T2 p1                 ) { ... }

You might want to combine the above two approaches as there is a certain amount of overhead in the use of params T[]. This approach lets the compiler choose the most suitable overload, thus avoiding the expense of constructing the params array:

public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1                 ) { ... }
public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 , T2 p2         ) { ... }
public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 , T2 p2 , T2 p3 ) { ... }
public TResult Foo<T1,T2,TResult>( T1 p0 , params T2[] p1        ) { ... }

You could use methods with default values, something like:

public TResult Foo<T1,T2,TResult>( T1 p0 , T2 p1 = default(T2) , T2 p2 = default(T2) ) { ... }

One should note that there are pitfalls when you have method overloads involving optional parameters:

There more than one way to do it.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top