在C#中重载函数调用操作符

是否可以在C#中重载默认函数运算符(()运算符)? 如果是这样 – 怎么样? 如果没有,是否有解决方法来创建类似的影响?

谢谢,
阿萨夫

编辑:
我试图给一个类一个默认运算符,类似于:

class A { A(int myvalue) {/*save value*/} public static int operator() (A a) {return a.val;} .... } ... A a = new A(5); Console.Write(A()); 

编辑2:
我已经阅读了规范,我知道没有直接的方法来做到这一点。 我希望有一个解决方法。

编辑3:动机是使一个类或一个实例表现得像一个函数,以创建一个方便的日志记录界面。 顺便说一句,这在C ++中是可行和合理的。

不, ()不是运算符,因此不能重载。 这是不正确的,请参阅下面的Eric Lippert的评论 )括号是C# 语法的一部分 ,用于表示传递给方法的一组参数。 ()只是表明有问题的方法没有指定正式参数,因此不需要参数。

你想做什么? 也许如果你给出一个小问题的例子,我们就可以帮助解决问题了。

编辑:好的,我知道你现在得到了什么。 您总是可以像这样创建一个映射到实例上的方法的委托(假设class A定义了这样的方法: public void Foo() { } ):

 Action action = someA.Foo; 

然后你可以用这样的简单语法调用委托:

 action(); 

不幸的是(或者不是,取决于你的偏好)这很接近C#会让你得到这种语法。

那没有。 C#规范的第7.2.2节将可重载运算符定义为:

UNARY:+ – ! 〜++ – 真假
二进制:+ – * /%&| ^ << >> ==!=> <> = <=

无论如何,你的可读性会变得一团糟。 也许有另一种方法来实现你想要做的事情?

是否可以在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 。 这将要求您调用命名方法,并且不允许您尝试实现的短语法。 但它会松散地绑定,并且可能会优雅地失败。

使用更简单的.Netfunction

您可以查看其他解决方案。

接口:

使用标准化方法创建基本ILoggable接口。

扩展方法:

使用.Log() 扩展方法创建日志记录界面。 扩展方法可以是通用的,并且可以采用基类型,如object ,因此您不必修改现有的类来支持它。

覆盖ToString

记录意味着您正在尝试将数据转换为文本(因此可以记录)。 考虑到这一点,您可以简单地覆盖ToString方法。

您可以在所有这些情况下创建方法重载,但它们将强烈绑定到每种类型。 您在原始问题中请求的解决方案也受到类型的严格约束,因此这些解决方案并非真正处于劣势。

现有解决方案

我见过的现有.Net日志库依赖于覆盖ToString运算符。 正如我上面所说,这是有道理的,因为你的日志是文本的。

有关.Net日志的先前技术,请参阅以下库:

关于内置委托类型的注意事项

确保在所有这些情况下使用内置委托类型 ,而不是定义自己的委托类型。 最终会减少混乱,并要求您编写更少的代码。

 // function that returns void Action a1 = ...; Action a2 = ...; Action a3 = ...; // etc // function that returns a value Func f1 = ...; Func f2 = ...; Func f3 = ...; // etc 

是的,这可以通过dynamic类型完成(更多信息可在此处找到)。

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

dynamic类型意味着完全在运行时假定类型,从而允许几乎与对象的每次交互都具有更大的灵活性。

我假设您打算在Console.Write行中使用而不是A

你不能重载() ,但是你可以从一个方法中获取一个对象并创建一个委托(类似于一个函数指针):

 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' // ... } } 

现在, DoSomething是一个接受int并返回Baz ,它与委托类型Func兼容。
(对于返回void的方法,有ActionAction ,并且分别取一个或一个参数。还有Func和用于接受更多参数的变体。)

所以你可以有一个方法,它接受一个Func ,并对它做任何事情:

 void Frob(Func f) { Baz b = f(5); // do whatever with your baz } 

最后,创建委托并将其传递给Frob如下:

 Foo foo = new Foo(); Func f = new Func(foo.DoSomething); Frob(f); 

这对任何事都有帮助吗? 我还不清楚你想要完成什么。

您提到要进行日志记录,以下是如何使用委托来执行此操作:

 FooBar MyAlgorithm(Foo paramA, Bar paramB, Actions logger) { logger("Starting algorithm.") FooBar result = ...; logger("Finished algorithm."); return result; } 

运行时,您可以登录到控制台:

 MyAlgorithm(a, b, (text) => Console.WriteLine(text)); 

或者登录到Windows窗体TextBox:

 TextBox logbox = form.GetLogTextBox(); MyAlgorithm(a, b, (text) => logbox.Text += text + Environment.NewLine); 

检查隐式转换 。 另一种选择是explict转换 ,但是你需要转换对象类型。

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