tags:

views:

326

answers:

3

Hi!

I'm trying to get the following thing done:

public delegate void SomeMethod(params object[] parameters);

That's my delegate. And i have some method that will run this SomeMethod delegate (whatever's passed) and return me the timespan of execution.

   public TimeSpan BenchmarkMethod(SomeMethod someMethod, params object[] parameters)
    {
        DateTime benchmarkStart = DateTime.Now;

        someMethod(parameters);

        DateTime benchmarkFinish = DateTime.Now;
        return benchmarkFinish - benchmarkStart;
    }

Also i have some method:

public abstract void InsertObjects (Company c);

So, i declare it:

SomeMethod dlg = new SomeMethod(InsertObjects);
TimeSpan executionTime = BenchmarkMethod(dlg, c);

But it doesn't run, saying that No overload for 'InsertObjects' matches delegate 'TestFactory.MeasuringFactory.SomeMethod'. Is there any way to do it?.. Or i should change all the methods of mine to accept params object[] as an argument?..

+2  A: 

Matching params parameters is compiler magic, and no such magic exists for delegates. It will match a method that has an array of a compatible type in the right position, but nothing else.

So yes, you need to either change all your methods, or use anonymous methods as wrappers, like this:

SomeMethod dlg = new SomeMethod(delegate(Object[] parameters)
{
    InsertObjects((Company)parameters[0]);
};
Lasse V. Karlsen
+4  A: 

Strictly no, the method signature must exactly match the signature specified by the delegate (except for covariant matching). However, you can create an object[] array and feed to Delegate.DynamicInvoke(object[] args).

Edit:

If you have information about the method to be called, you can use MethodBase.GetParameters().Length to obtain the number of arguments, so you can correctly size the untyped argument array.

For benchmarking, though, I think you are better off using an abstract base class that implements the necessary benchmarking ops:

abstract class Benchmark
{
    TimeSpan Run()
    {
     Stopwatch swatch = Stopwatch.StartNew();
     // Optionally loop this several times and divide elapsed time by loops:
     RunMethod();
     swatch.Stop();
     return swatch.Elapsed;
    }

    ///<summary>Override this method with the code to be benchmarked.</summary>
    protected abstract void RunMethod()
    {
    }
}

Virtual method dispatches have comparable latency to delegates, and much better than dynamic invocation.

Cecil Has a Name
thank you!) sorry for not marking your great answer. just forgot of it. i've used ur code a long time ago but marked it just today)
ifesdjeen
Thanks for accepting as answer. Not everyone cares to come back to a "stack" on this site to follow up the latest changes.
Cecil Has a Name
A: 

Will Delegate with params keyword match any method?

No. They still have to respect type variance.

params is only syntatic sugar for saying that from that point and beyond, all parameters of the call site are considered to be part of the same array on the method.

So, for a method defined as:


TimeSpan BenchmarkMethod(SomeMethod someMethod, params Company[] parameters)

You can do:


Company company1 = null;
Company company2 = null;

//In BenchmarkMethod, company1 and company2 are considered to be part of 
//parameter 'parameters', an array of Company;
BenchmarkMethod(dlg, company1, company2);

but not:


Company company1 = null;
object company3 = new Company();

BenchmarkMethod(dlg, company1, company3);

Because, although company3 contains a Company at runtime, it's static type is object.

So now we know that params simply defines an array on a method, that allows you to use a more convenient syntax at the call site.

Now let's go on with the real reason for your code not working as you expected: Type variance

Your delegate is defined as:


public delegate void SomeMethod(params object[] parameters);

and you target method as:


public abstract void InsertObjects (Company c);

When invoking the delegate:


SomeMethod dlg = new SomeMethod(InsertObjects);
TimeSpan executionTime = BenchmarkMethod(dlg, c);

You are essentialy saying you can call InsertObjects passing it an array with any type of object instead of an object of type Company.

That of course is not allowed by the compiler.

If instead, you invert the types of the delegate and the target method such as in:


public delegate void SomeMethod(params Company[] parameters);

public TimeSpan BenchmarkMethod(SomeMethod someMethod, params Company[] parameters) {
    DateTime benchmarkStart = DateTime.Now;
    someMethod(parameters);
    DateTime benchmarkFinish = DateTime.Now;
    return benchmarkFinish - benchmarkStart;
}

public void InsertObjects(object c) {
    Console.WriteLine(c);
}

Then it will compile, because you'll be passing an array of Customer to a method that accepts any kind of object.

Conclusion: params does not affect type variance rules.

Alfred Myers