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

时间:2010-03-15 20:15:31

标签: 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());

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

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

7 个答案:

答案 0 :(得分:17)

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

  

UNARY:+ - ! 〜++ - 是的   false
二进制:+ - * /%   &安培; | ^<< >> ==!=> < > =< =

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

答案 1 :(得分:13)

  

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

在C#中,只有方法和委托才能作为函数调用。在C#中,代表与您要问的C ++ function objects一样接近。

  

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

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

使用重载转化运算符

定义:

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() extension methods创建您的日志记录界面。扩展方法可以是通用的,并且可以采用基类型,例如object,因此您不必修改现有的类来支持它。

覆盖ToString

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

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

现有解决方案

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

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

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

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

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

答案 2 :(得分:8)

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

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

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

Action action = someA.Foo;

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

action();

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

答案 3 :(得分:4)

是的,这绝对可以使用dynamic类型完成(找到更多信息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());

dynamic类型意味着该类型完全在运行时被假定,几乎可以与对象的每次交互具有更大的灵活性。

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

答案 4 :(得分:0)

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

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<int, Baz>兼容。
(对于返回void的方法,有ActionAction<T>,并且分别取一个或一个参数。还有Func<T1, T2, TResult>和用于接受更多参数的变体。)

所以你可以有一个方法需要Func<int, Baz>,并用它做任何事情:

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

最后,创建委托并将其传递给Frob就像这样:

Foo foo = new Foo();
Func<int, Baz> f = new Func<int, Baz>(foo.DoSomething);
Frob(f);

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

答案 5 :(得分:0)

您提到要进行日志记录,以下是代理人如何做到这一点:

FooBar MyAlgorithm(Foo paramA, Bar paramB, Actions<string> 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);

答案 6 :(得分:0)

结帐implicit conversion。另一个选项是explict conversion,但是你需要转换对象类型。

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