2010-03-15 92 views
21

是否有可能重載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++中是可行和合理的。

+0

該操作符將如何使用?你能發表一個使用'A'類型的實例的代碼示例嗎? – 2010-03-15 20:25:14

+3

當涉及到C++ – 2010-03-15 20:44:24

+4

@Isak時,請不要混淆「可行」與「合理」,我不認爲我是。 – 2010-03-15 21:41:10

回答

8

不,()不是運營商,所以它不能超載。這是不正確的,請參閱下面的Eric Lippert的評論)括號是C#的語法的一部分,用於表示傳遞給方法的一組參數。 ()只是簡單地表明該方法沒有指定任何形式參數,因此不需要參數。

你到底想幹什麼?也許如果你舉了一個小問題的例子,我們可以幫助解決問題。

編輯:好吧,我明白你現在在看什麼。你總是可以創建一個映射到在這樣的實例方法的委託(給定class A定義這樣的方法:public void Foo() { }):

Action action = someA.Foo; 

然後,你可以用一個簡單的語法,這樣調用委託:

action(); 

不幸的是(或不是,取決於您的偏好),這與C#會讓你接近這種語法非常接近。

+9

函數調用操作符最*肯定*是操作符。它不是一個可以重載的操作員,但它絕對是一個操作員;它具有操作數,它具有運算符優先級,它具有關聯性,它被記錄爲運算符,被解析爲運算符並被編碼爲運算符。 – 2010-03-16 05:56:11

+0

謝謝埃裏克 - 正式注意! – 2010-03-16 14:03:34

16

沒有。 C# specification的第7.2.2節將可重載運營商定義爲:

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

你的可讀性會去地獄呢。也許還有另一種方法來實現你想要做的事情?

+0

+1引用規範。 – 2010-03-15 20:34:20

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

這是否有助於任何事情?我仍然不清楚你想完成什麼。

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 logbox = form.GetLogTextBox(); 
MyAlgorithm(a, b, (text) => logbox.Text += text + Environment.NewLine); 
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 
    } 
} 
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](); 

索引器版本有古怪的語法,但這樣做在你原來的問題的例子(WRT標準C#成語)。它也是有限的,因爲你不能定義一個採用零參數的索引器。

如果你想要一個無參數函數的解決方法是製作一個虛擬參數(可能是object)並向它傳遞一個丟棄值(可能是null)。但是這個解決方案真的很糟糕,並且需要你仔細研究以瞭解使用情況。如果你想要一個重載你的虛擬類型的單個參數,它也會中斷。

的動機是使一個類或實例表現得像一個函數,使一個方便的登錄界面

有了這個動機在腦海,我可能會建議你放棄上述這些選項。對於這個問題他們是矯枉過正的。如果你擴大你允許的解決方案,你可能會發現你更喜歡其中一個。

使用動態而不是

我提到的其他方法需要強類型,並且絕不普通。這可能是你描述的一個巨大的缺點。

如果你想要較弱的綁定,你可以看看Dynamic。這將要求您調用命名方法,並且不會允許您嘗試實現的簡短語法。但它會鬆散地束縛,並可能失敗。

使用更簡單。網功能代替

還有其他的解決方案,你可以看看。

接口:

創建基礎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 
1

是的,這可絕對可以與dynamic類型來完成的(更多信息發現here)。

using System.Dynamic.DynamicObject 

class A : DynamicObject 
{ 
    private int val; 

    A(int myvalue) 
    { 
     this.val = val; 
    } 

    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類型意味着類型完全假定在運行時允許更大的flexability幾乎所有與對象相互作用。

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

+0

我假設你打算說'this.val = myvalue;' – NetMage 2017-07-19 19:15:38