por - Función sobrecarga operador de llamada en C#
sobrecarga de metodos swift (7)
¿Es posible sobrecargar el operador de la función predeterminada (el operador ()) en C #? ¿Si es así, cómo? Si no es así, ¿hay una solución para crear un efecto similar?
Gracias,
Asaf
EDITAR:
Estoy tratando de darle a una clase un operador predeterminado, algo así como:
class A {
A(int myvalue) {/*save value*/}
public static int operator() (A a) {return a.val;}
....
}
...
A a = new A(5);
Console.Write(A());
EDIT 2:
He leído la especificación y entiendo que no hay una manera directa de hacer esto. Esperaba que hubiera una solución.
EDIT 3: la motivación es hacer que una clase, o una instancia se comporte como una función, para hacer una interfaz de registro conveniente. Por cierto, esto es factible y razonable en C ++.
¿Es posible sobrecargar el operador de la función predeterminada (el operador ()) en C #? ¿Si es así, cómo?
En C #, solo los métodos y los delegados tienen sentido invocar como funciones. En C #, los delegados están tan cerca como usted llega a los objetos de la función C ++ que está preguntando.
Si no es así, ¿hay una solución para crear un efecto similar?
No puede obtener exactamente lo que desea, pero puede acercarse vagamente (en términos de sintaxis).
Usando operadores de conversión sobrecargados
Definición:
public static implicit operator DelegateType(TypeToConvert value)
{
return value.TheMethodToCall;
}
Uso:
var someValue = new TypeToConvert();
DelegateType someValueFunc = someValue;
someValueFunc(value1);
Esto obtiene la sintaxis final que desea, pero requiere que haga una conversión intermedia donde especifique el tipo.
Puedes hacer esto por:
- Asignar su valor a una variable local, o pasarlo a una función que toma un tipo de delegado coincidente (conversión implícita)
- Casting (conversión explícita)
Utilizando indexadores
Definición:
public DelegateType this[ParameterType1 value1, ...]
{
get
{
// DelegateType would take no parameters, but might return a value
return () => TheMethodToCall(value1);
}
}
Uso:
variable[value1]();
La versión del indexador tiene una sintaxis de aspecto gracioso, pero también lo tiene el ejemplo en su pregunta original (escriba los modismos estándar de C #). También es limitado, porque no puede definir un indexador que tome cero parámetros.
Una solución alternativa si desea una función sin parámetros es hacer un parámetro ficticio (probablemente de tipo object
) y pasarle un valor de descarte (probablemente null
). Pero esa solución es realmente grave, y requiere que mire debajo del capó para comprender el uso. También se rompería si alguna vez quisieras una sobrecarga que tomara un solo parámetro de tu tipo ficticio.
La motivación es hacer que una clase, o una instancia se comporte como una función, para hacer una interfaz de registro conveniente
Con esta motivación en mente, podría sugerirle que abandone las opciones anteriores. Ellos son una exageración para este problema. Si amplia sus soluciones permitidas, es posible que encuentre uno de ellos mejor.
Usando dinámico en su lugar
Los otros métodos que mencioné requieren una tipificación fuerte y no son genéricos de ninguna manera. Esto puede ser una gran desventaja para lo que está describiendo.
Si quieres un enlace más débil, puedes buscar en Dynamic
. Esto requeriría que invoque métodos con nombre y no permitiría la sintaxis corta que intenta implementar. Pero estaría ligeramente ligado y podría fallar con gracia.
Usando funciones de .Net más simples en su lugar
Hay otras soluciones que podría considerar.
Interfaces:
Cree una interfaz base de ILoggable
, con métodos estandarizados.
Métodos de extensión:
Cree su interfaz de registro con los métodos de extensión .Log()
. Los métodos de extensión se pueden hacer genéricos y pueden tomar tipos de base, como object
, para que no tenga que modificar sus clases existentes para admitir esto.
Anular ToString
:
El registro implica que está intentando convertir sus datos en texto (para que puedan registrarse). Teniendo esto en cuenta, simplemente podría anular el método ToString
.
Puede crear sobrecargas de métodos en todos estos casos, pero estarán fuertemente vinculados a cada tipo. Sin embargo, la solución que solicitó en su pregunta original también está fuertemente ligada al tipo, por lo que estas soluciones no están realmente en desventaja.
Soluciones existentes
Las bibliotecas de registro de .Net existentes que he visto dependen de que usted ToString
operador ToString
. Como dije anteriormente, esto tiene sentido, porque su registro es textual.
Para ilustraciones anteriores en el registro de .Net, vea estas bibliotecas:
- http://logging.apache.org/log4net/
- http://nlog-project.org/
- http://docs.castleproject.org/Windsor.Logging-Facility.ashx (un registrador genérico)
Nota sobre los tipos de delegados incorporados
Asegúrese de usar los tipos de delegado incorporados en todos estos casos, en lugar de definir sus propios tipos de delegado. Al final será menos confuso y requerirá que escriba menos código.
// 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
Echa un vistazo a la conversión implícita . Otra opción sería la conversión explícita , pero luego necesitaría convertir el tipo de objeto.
public class A
{
public A(int myValue)
{
this.MyValue = myValue;
}
public int MyValue { get; private set; }
public static implicit operator int(A a)
{
return a.MyValue;
}
public static implicit operator A(int value)
{
return new A(value);
}
// you would need to override .ToString() for Console.WriteLine to notice.
public override string ToString()
{
return this.MyValue.ToString();
}
}
class Program
{
static void Main(string[] args)
{
A t = 5;
int r = t;
Console.WriteLine(t); // 5
}
}
Menciona que desea hacer el registro, aquí es cómo podría hacerlo con los delegados:
FooBar MyAlgorithm(Foo paramA, Bar paramB, Actions<string> logger) {
logger("Starting algorithm.")
FooBar result = ...;
logger("Finished algorithm.");
return result;
}
Cuando ejecutes esto puedes iniciar sesión en la consola:
MyAlgorithm(a, b, (text) => Console.WriteLine(text));
O inicie sesión en un TextBox de Windows Forms:
TextBox logbox = form.GetLogTextBox();
MyAlgorithm(a, b, (text) => logbox.Text += text + Environment.NewLine);
No hay. La sección 7.2.2 de la especificación C # define los operadores recargables como:
UNARIO: + -! ~ ++ - verdadero falso
BINARIO: + - * /% & | ^ << >> ==! => <> = <=
Tu legibilidad iría al infierno de todos modos. Tal vez hay otra manera de lograr lo que estás tratando de hacer?
No puede sobrecargar ()
, pero puede tomar un objeto y hacer que un delegado (similar a un puntero de función) salga de uno de sus métodos:
class Foo
{
private Bar bar;
// etc.
public Baz DoSomething(int n)
{
// do something, for instance,
// get a Baz somehow from the ''bar'' field and your int ''n''
// ...
}
}
Ahora, DoSomething
es un método que toma un int
y devuelve un Baz
, que es compatible con el tipo de delegado Func<int, Baz>
.
(Hay Action
y Action<T>
para los métodos que devuelven void y no toman, respectivamente, ningún argumento. También hay Func<T1, T2, TResult>
y variantes para aceptar más argumentos).
Así que podrías tener un método que tome un Func<int, Baz>
, y haga lo que sea con él:
void Frob(Func<int, Baz> f)
{
Baz b = f(5);
// do whatever with your baz
}
Finalmente, crear el delegado y Frob
a Frob
es así:
Foo foo = new Foo();
Func<int, Baz> f = new Func<int, Baz>(foo.DoSomething);
Frob(f);
¿Esto ayuda algo en absoluto? Todavía no estoy muy claro qué es exactamente lo que quieres lograr.
Sí, esto se puede hacer absolutamente con el tipo dynamic
(más información se encuentra here ).
using System.Dynamic.DynamicObject
class A : DynamicObject
{
private int val;
A(int myvalue)
{
this.val = myvalue;
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) {
result = this.val;
return true;
}
}
...
dynamic a = new A(5);
Console.Write(a());
El tipo dynamic
significa que el tipo se asume completamente en el tiempo de ejecución, lo que permite una mayor flexibilidad para casi todas las interacciones con el objeto.
He asumido que pretendías usar a
lugar de A
en la línea de Console.Write
.
No, ( Esto es incorrecto, consulte el comentario de Eric Lippert a continuación ) Los paréntesis son parte de la sintaxis de C # que se usan para expresar un conjunto de argumentos que se pasan a un método. ()
no es un operador, por lo que no se puede sobrecargar. ()
simplemente indica que el método en cuestión no especificó parámetros formales y, por lo tanto, no requiere argumentos.
¿Que estás tratando de hacer? Quizás si nos da un pequeño ejemplo del problema, podríamos ayudarlo con una solución.
Edit: está bien, veo a qué te refieres ahora. Siempre se puede crear un delegado que se asigne al método en la instancia como esta (dado que la class A
define un método como este: public void Foo() { }
):
Action action = someA.Foo;
Entonces podrías invocar al delegado con una sintaxis simple como esta:
action();
Desafortunadamente (o no, dependiendo de su preferencia) esto es casi tan cercano como C # le permitirá llegar a este tipo de sintaxis.