Указатель функции С# в перегруженной функции
У меня есть 2 перегруженные функции С#:
private void _Insert(Hashtable hash, string tablename, Func<string, object[], SqlCommand> command)
private void _Insert(Hashtable hash, string tablename, Func<string, object[], OleCommand> command)
В основном используется OleCommand и другой SqlCommand для возвращаемого значения функции.
Но уродство в этом заключается в том, что я должен отбросить указатель на правильный тип, даже если я чувствую, что компилятор должен решить его без проблем:
class RemoteDatabase
{
public SqlCommand GetCommand(string query, object[] values);
}
_Insert(enquiry, "Enquiry", (Func<string, object[], SqlCommand>)(_RemoteDatabase.GetCommand));
Есть ли способ сказать компилятору умнее, чтобы мне не пришлось делать литье типов? Или я сделал что-то неправильно?
EDIT:
Добавлена щедрость, потому что мне очень интересно учиться. Спасибо за любой совет.
Ответы
Ответ 1
Не отвечая на ваш вопрос напрямую, я натолкнулся на следующее при написании тестового примера, вы можете его скомпилировать, завернув вызов в другой лямбда. Что удаляет явное приведение за счет другого вызова метода (по крайней мере, я так думаю, еще не посмотрел на IL)
class RemoteDatabase
{
public int GetCommand(){return 5;}
}
class Program
{
static void Main(string[] args)
{
var rd = new RemoteDatabase();
// Overloaded(1, rd.GetCommand); // this is a compile error, ambigous
Overloaded(1, () => rd.GetCommand()); // this compiles and works
Console.ReadLine();
}
static void Overloaded(int paramOne, Func<int> paramFun)
{
Console.WriteLine("First {0} {1}", paramOne, paramFun());
}
static void Overloaded(int paramOne, Func<string> paramFun)
{
Console.WriteLine("Second {0} {1}", paramOne, paramFun());
}
}
Edit-
Я нашел этот пост Эрика Липперта, который отвечает на этот вопрос
Интересный факт: правила преобразования для лямбда типы возврата счета. Если вы скажете Foo (() = > X()), то мы делаем правильный вещь. Тот факт, что группы lambdas и группы имеют разные правила конвертирования довольно неудачны.
Ответ 2
EDIT: Это вызвано процессом, определенным в разделе "Разрешение перегрузки" спецификации С#. Когда он получает набор применимых членов-кандидатов, он не может выбрать "лучшую функцию" , потому что он не принимает тип возвращаемого значения в учетной записи во время разрешения перегрузки. Так как он не может выбрать наилучшую функцию, то двусмысленный метод вызывает ошибку вызова по спецификации.
Однако, если ваша цель - упростить вызовы методов и избежать длительной кастинга для func, вы можете использовать generics для и усложнять метод _Insert немного как это, например:
public void Main()
{
_Insert(new Hashtable(), "SqlTable", F1);
_Insert(new Hashtable(), "OleTable", F2);
}
private static SqlCommand F1(string name, object[] array)
{
return new SqlCommand();
}
private static OleDbCommand F2(string name, object[] array)
{
return new OleDbCommand();
}
private void _Insert<T>(Hashtable hash, string tablename, Func<string, object[], T> command)
{
if (typeof(T) == typeof(SqlCommand)) {
SqlCommand result = command(null, null) as SqlCommand;
}
else if (typeof(T) == typeof(OleDbCommand)) {
OleDbCommand result = command(null, null) as OleDbCommand;
}
else throw new ArgumentOutOfRangeException("command");
}
Обратите внимание на упрощенные вызовы методов
_Insert(new Hashtable(), "OleTable", F1);
_Insert(new Hashtable(), "OleTable", F2);
которые компилируют только тонкие
Ответ 3
Можете ли вы использовать Func<string, object[], DbCommand>
? Это также позволит вам избавиться от перегрузки и понадобится только одна функция.
Ответ 4
так как возвращаемое значение действительно не учитывается, как об одном из них: (примечание: я написал тестовый проект с немного другим кодом, поэтому с ним могут возникнуть какие-то проблемы, но это должно дать идея...)
public class RemoteDatabase
{
// changed to private, referenced by CommandWay1
private SqlCommand GetCommand(string query, object[] values)
{
/* GetCommand() code */
}
public Func<string, object[], SqlCommand> CommandWay1
{
get
{
return (q,v) => GetCommand(q,v);
}
}
// or, you could remove the above and just have this,
// with the code directly in the property
public Func<string, object[], SqlCommand> CommandWay2
{
get
{
return
(q,v) =>
{
/* GetCommand() code */
};
}
}
тогда я смог получить каждый из них для компиляции без кастинга:
_Insert(enquiry, "Enquiry", (q,v) => _RemoteDatabase.GetCommand(q,v));
_Insert(enquiry, "Enquiry", _RemoteDatabase.CommandWay1);
_Insert(enquiry, "Enquiry", _RemoteDatabase.CommandWay2);