Возможно ли перегрузить оператор функции по умолчанию (оператор ()) в C #? Если так - как?
В C # только методы и делегаты имеют смысл вызывать как функции. В C # делегаты так же близки, как и к объектам функций C ++ , о которых вы спрашиваете.
Если нет, есть ли обходной путь для создания аналогичного эффекта?
Вы не можете получить именно то, что вы хотите, но вы можете приблизиться немного (по синтаксису).
Использование перегруженных операторов преобразования
Определение:
public static implicit operator DelegateType(TypeToConvert value)
{
return value.TheMethodToCall;
}
Использование:
var someValue = new TypeToConvert();
DelegateType someValueFunc = someValue;
someValueFunc(value1);
Получается последний желаемый синтаксис, но требуется промежуточное преобразование, в котором вы указываете тип.
Вы можете сделать это:
- Присвоение значения локальной переменной или передача его функции, принимающей соответствующий тип делегата (неявное преобразование)
- Приведение (явное преобразование)
Использование индексаторов
Определение:
public DelegateType this[ParameterType1 value1, ...]
{
get
{
// DelegateType would take no parameters, but might return a value
return () => TheMethodToCall(value1);
}
}
Использование:
variable[value1]();
Версия индексатора имеет забавный синтаксис, как и пример из вашего исходного вопроса (со стандартными идиомами C #). Это также ограничено, потому что вы не можете определить индексатор, который принимает нулевые параметры.
Обходной путь, если вам нужна функция без параметров, - создать фиктивный параметр (вероятно, типа object
) и передать ему однозначное значение (вероятно, null
). Но это решение действительно грубое и требует от вас заглянуть под капот, чтобы понять использование. Это также сломало бы, если бы вы когда-либо хотели перегрузку, которая принимала бы единственный параметр вашего фиктивного типа.
Мотивация состоит в том, чтобы заставить класс или экземпляр вести себя как функция, чтобы сделать удобный интерфейс ведения журнала
Учитывая эту мотивацию, я мог бы предложить вам отказаться от указанных выше вариантов. Они излишни для этой проблемы. Если вы расширите допустимые решения, вам может показаться, что вам больше нравится один из них.
Использование динамического вместо
Другие методы, которые я упомянул, требуют строгой типизации и ни в коем случае не являются общими. Это может быть огромным недостатком для того, что вы описываете.
Если вы хотите более слабую привязку, вы можете посмотреть Dynamic
. Это потребует от вас вызова именованных методов и не позволит использовать короткий синтаксис, который вы пытаетесь реализовать. Но оно будет свободно связано и может изящно провалиться.
Использование более простых функций .Net вместо
Существуют и другие решения, которые вы можете посмотреть.
Интерфейсы:
Создание базового интерфейса ILoggable
с использованием стандартных методов.
Методы расширения:
Создайте свой интерфейс ведения журнала с помощью .Log()
методов расширения . Методы расширения могут быть сделаны универсальными и могут принимать базовые типы, такие как object
, поэтому вам не придется изменять существующие классы для поддержки этого.
Переопределить ToString
:
Ведение журнала подразумевает, что вы пытаетесь преобразовать ваши данные в текст (чтобы их можно было зарегистрировать). Имея это в виду, вы можете просто переопределить метод ToString
.
Вы можете создавать перегрузки методов во всех этих случаях, но они будут строго привязаны к каждому типу. Решение, которое вы запрашивали в исходном вопросе, также сильно привязано к типу, так что эти решения на самом деле не в невыгодном положении.
Существующие решения
Существующие библиотеки журналов .Net, которые я видел, полагаются на то, что вы переопределяете оператор ToString
. Как я уже говорил выше, это имеет смысл, потому что ваш журнал текстовый.
Предыдущие версии журналов .Net см. В следующих библиотеках:
Примечание о встроенных типах делегатов
Убедитесь, что вы используете встроенные типы делегатов во всех этих случаях вместо определения собственных типов делегатов. Это будет менее запутанным в конце, и потребует от вас писать меньше кода.
// function that returns void
Action a1 = ...;
Action<TParameter1> a2 = ...;
Action<TParameter1, TParameter2> a3 = ...;
// etc
// function that returns a value
Func<TReturn> f1 = ...;
Func<TParameter1, TReturn> f2 = ...;
Func<TParameter1, TParameter2, TReturn> f3 = ...;
// etc