是否可以在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 ++中是可行和合理的。
答案 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的方法,有Action
和Action<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
}
}