Будет ли делегат с ключевым словом params соответствовать любому методу? - PullRequest
3 голосов
/ 30 августа 2009

Я пытаюсь сделать следующее:

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

Это мой делегат. И у меня есть какой-то метод, который запустит этот делегат SomeMethod (что бы ни прошло) и вернет мне время выполнения.

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

        someMethod(parameters);

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

Также у меня есть метод:

public abstract void InsertObjects (Company c);

Итак, я заявляю это:

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

Но он не запускается, говоря, что перегрузка для 'InsertObjects' не соответствует делегату 'TestFactory.MeasuringFactory.SomeMethod'. Есть ли способ сделать это? .. Или я должен изменить все мои методы, чтобы принимать params object [] в качестве аргумента? ..

Ответы [ 3 ]

4 голосов
/ 30 августа 2009

Строго нет, подпись метода должна точно соответствовать подписи, указанной делегатом (кроме ковариантного сопоставления). Однако вы можете создать массив object[] и передать его в Delegate.DynamicInvoke(object[] args).

Edit:

Если у вас есть информация о вызываемом методе, вы можете использовать MethodBase.GetParameters().Length для получения количества аргументов, чтобы вы могли правильно определить размер массива нетипизированных аргументов.

Однако для бенчмаркинга, я думаю, вам лучше использовать абстрактный базовый класс, который реализует необходимые операции бенчмаркинга:

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()
    {
    }
}

Отправка виртуальных методов имеет сравнимую задержку с делегатами и намного лучше, чем динамический вызов.

4 голосов
/ 30 августа 2009

Будет ли делегирование с ключевым словом params совпадать с любым методом?

Нет. Они по-прежнему должны учитывать дисперсию типов.

params - это только синтетический сахар, говорящий о том, что с этого момента и далее все параметры сайта вызова считаются частью одного и того же массива в методе.

Итак, для метода, определенного как:


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

Вы можете сделать:


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);

, но не:


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

BenchmarkMethod(dlg, company1, company3);

Поскольку хотя company3 содержит Company во время выполнения, ее статическим типом является object.

Итак, теперь мы знаем, что params просто определяет массив метода, который позволяет вам использовать более удобный синтаксис на сайте вызова.

Теперь давайте рассмотрим реальную причину того, что ваш код работает не так, как вы ожидали: Дисперсия типа

Ваш делегат определен как:


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

и вы выбираете метод как:


public abstract void InsertObjects (Company c);

При вызове делегата:


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

Вы, по сути, говорите, что можете вызывать InsertObjects, передавая ему массив с любым типом объекта вместо объекта типа Company.

Это, конечно, не разрешено компилятором.

Если вместо этого вы инвертируете типы делегата и целевой метод, например:


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);
}

Затем он скомпилируется, потому что вы передадите массив Customer методу, который принимает любой тип объекта.

Вывод: params не влияет на правила дисперсии типов.

2 голосов
/ 30 августа 2009

Соответствие params параметрам - магия компилятора, и для делегатов такой магии не существует. Он будет соответствовать методу, у которого в правильном положении находится массив совместимого типа, но больше ничего.

Так что да, вам нужно либо изменить все ваши методы, либо использовать анонимные методы в качестве обёрток, например:

SomeMethod dlg = new SomeMethod(delegate(Object[] parameters)
{
    InsertObjects((Company)parameters[0]);
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...