C Pottinger Ответов: 2

Изменение способа работы функции


Привет Гуру Кодового Проекта

Я знаю, что делегаты могут работать со значениями, которые известны в момент вызова делегата - это просто параметр
напр.
   Func<double, double> areaOfCircle = r => Math.PI * Math.Pow(r, 2); 
   Func<double, double> diameterOfCircle = r => 2 * Math.PI * r;
   
   var iNeed = diameterOfCircle;
// -----------------------------
   Console.WriteLine(iNeed(5));    // output is 31.4159265358979
   Console.WriteLine(iNeed(10));   //           62.8318530717959
		
   iNeed = areaOfCircle; 
// ---------------------
   Console.WriteLine(iNeed(5));    // output is 78.5398163397448
   Console.WriteLine(iNeed(10));   //           314.159265358979

Мне интересно, существует ли какой-либо способ динамического создания делегатов, чтобы каждый делегат работал с использованием значений, которые известны во время выполнения при создании делегата (а не при вызове делегата).

Итак, используя приведенный выше пример, есть ли способ сказать areaOfCircle использовать какой-то другой фактор, а не pi? Что-то похожее на
   Func<double, double> areaOfCircle = r => ::factor:: * Math.Pow(r, 2); 
   Func<double, double> diameterOfCircle = r => 2 * ::factor:: * r;
   
   var iNeed = diameterOfCircle(factor::3);   
// ----------------------------------------
   Console.WriteLine(iNeed(5));    // output is 30
   Console.WriteLine(iNeed(10));   //           60
		
   iNeed = areaOfCircle(factor::3.14); 
// -----------------------------------
   Console.WriteLine(iNeed(5));    // output is 78.5
   Console.WriteLine(iNeed(10));   //           314

Опять же, я знаю, что простой пример, подобный этому, легко разрешим, передав фактор каждой функции в консоли.WriteLine звонит. Но вместо этого представьте, что присвоение iNeed и вызовы iNeed происходят в отдельных методах или даже классах; что как только iNeed присваивается, он передается в качестве возвращаемого значения вызывающему классу, который вставляет его в список, который вызывается другим классом для выполнения функции... нормальные, повседневные вещи.

То, что я ищу, - это способ настроить функции для работы с определенным фактором, когда выполняется назначение iNeed. Разве такое возможно?

Что я уже пробовал:

Поиск в сети.
Чтение документации MS по делегатам, анонимным делегатам, динамическим переменным и т. д.

2 Ответов

Рейтинг:
2

Dave Kreskowiak

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

Делегат-это не что иное, как указатель на функцию. Список параметров делегата должен совпадать со списком параметров функции, на которую он указывает. Если это не так, вы можете разбалансировать стек, поскольку вызов выталкивает параметры в стек, а вызываемая функция выталкивает их из стека не так, как они были выталкнуты.

Эта концепция называется "самоизменяющийся код" и очень сложна для правильной реализации в производственном качественном коде. Я никогда не рекомендую этого делать.

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

double CalculateAreaCircle(double r, double factor = Math.PI);


Рейтинг:
19

Richard Deeming

Что-то вроде этого сработает:

Func<double, Func<double, double>> factoredAreaOfCircle = factor => r => factor * Math.Pow(r, 2);
Func<double, Func<double, double>> factoredDiameterOfCircle = factor => r => 2 * factor * r;

Func<double, double> iNeed = factoredDiameterOfCircle(3);
Console.WriteLine(iNeed(5));  // Output is 30
Console.WriteLine(iNeed(10)); // Output is 60
		
iNeed = factoredAreaOfCircle(3.14); 
Console.WriteLine(iNeed(5));  // Output is 78.5
Console.WriteLine(iNeed(10)); // Output is 314

Каррирование против применения частичной функции | блог кодирования Джона Скита[^]


C Pottinger

Т Е Р Р И Ф И К ! ! !
- Спасибо, Ричард. Это именно то, чего я хотел.

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

Func<double, Func<double, double>> areaOfCircle = factor => r => { Console.WriteLine("area factor is {0}", factor); return (double)(factor * Math.Pow(r, 2)); }; 
Func<double, Func<double, double>> diameterOfCircle = factor => r => { Console.WriteLine("diameter factor is {0}", factor); return (double)(2 * factor * r); };		

var funcList = new List<Func<double, double>>() 
{
	diameterOfCircle(Math.PI),
	areaOfCircle(Math.PI),
	diameterOfCircle(3),
	areaOfCircle(3.14),
};

foreach(var iNeed in funcList)
{
	//Console.WriteLine(iNeed(5));
	Console.WriteLine(iNeed(10));
}

Это сработало идеально.

И статья Джона Скита очень хорошо объясняла работу и предысторию.
Спасибо снова.