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

Asa*_*f R 28 c# operator-overloading

是否可以在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());
Run Code Online (Sandbox Code Playgroud)

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

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

Chr*_*ens 17

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

UNARY:+ - !〜++ - true false
BINARY:+ - */%&| ^ << >> ==!=> <> = <=

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

  • -1代表完全主观的陈述,即可读性将使所有人难以承受。可读性是想要此的唯一原因,因为总可以通过更复杂的方式来实现。除非您主张所有功能都应以这种方式调用,否则必须要做“ treatMeAsAFunction.Invoke()”比“ treatMeAsAFunction()”更具可读性。如果某物表示一个函数,则可以像调用一个函数一样有意义。 (5认同)

Mer*_*ham 14

是否可以在C#中重载默认函数运算符((运算符)?如果是这样 - 怎么样?

在C#中,只有方法和委托才能作为函数调用.在C#中,委托与您询问的C++ 函数对象一样接近.

如果没有,是否有解决方法来创建类似的影响?

你无法得到你想要的东西,但你可以模糊地接近(语法方面).

使用重载转换运算符

定义:

public static implicit operator DelegateType(TypeToConvert value)
{
    return value.TheMethodToCall;
}
Run Code Online (Sandbox Code Playgroud)

用法:

var someValue = new TypeToConvert();
DelegateType someValueFunc = someValue;
someValueFunc(value1);
Run Code Online (Sandbox Code Playgroud)

这将获得您想要的最终语法,但需要您在指定类型的情况下进行中间转换.

你可以这样做:

  • 将值赋给局部变量,或将其传递给采用匹配委托类型的函数(隐式转换)
  • 投射它(显式转换)

使用索引器

定义:

public DelegateType this[ParameterType1 value1, ...]
{
    get
    {
        // DelegateType would take no parameters, but might return a value
        return () => TheMethodToCall(value1);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

variable[value1]();
Run Code Online (Sandbox Code Playgroud)

索引器版本具有看似滑稽的语法,但原始问题中的示例也是如此(标准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
Run Code Online (Sandbox Code Playgroud)


And*_*are 8

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

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

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

Action action = someA.Foo;
Run Code Online (Sandbox Code Playgroud)

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

action();
Run Code Online (Sandbox Code Playgroud)

不幸的是(或者不是,取决于你的偏好),这几乎与C#一样可以让你获得这种语法.

  • 函数调用操作符**当然*是一个操作符.它不是一个可重载的运算符,但它绝对是一个运算符; 它具有操作数,它具有运算符优先级,具有关联性,它被记录为运算符,解析为运算符并且代码为运算符. (13认同)

Tom*_*Tom 6

是的,这绝对可以通过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());
Run Code Online (Sandbox Code Playgroud)

dynamic类型的装置,该类型完全假定在运行时允许更大的flexability几乎所有与对象相互作用。

我假设你想用的a,而不是AConsole.Write行。