2016-11-19 112 views
2

我寫簡單的解析器和接下來要實現兩個接口:泛型類型的引用對方

public interface IResult<TValue, TToken> 
    where TToken : ITokenizer<IResult<TValue, TToken>, TValue> 
{ 
    TToken Tokenizer { get; } 
    TValue Value { get; } 
} 

public interface ITokenizer<TResult, TValue> 
    where TResult : IResult<TValue, ITokenizer<TResult, TValue>> 
{ 
    TResult Advance(); 
} 

它旁邊目的:ITokenizer是通過標記分割字符串的不可變類。我們可以調用Advance方法並獲得Result:下一個令牌和下一個令牌。所以,我想在Result類中存儲令牌和標記器,並且希望爲此添加編譯時間約束。

現在我在構造這兩個接口的過程中出現編譯時錯誤。

我認爲下一個類可以實現與所有約束接口:

public class Result : IResult<string, Tokenizer> 
{ /* implement interface */} 

public class Tokenizer : ITokenizer<Result, string> 
{ /* implement interface */} 

誰能解釋什麼是錯的?也許爲什麼這是不可能的或如何使這段代碼正確?

P.S.對於我的任務,我可以簡單地使用IResult<TValue, TToken>接口沒有任何限制,但我可以實現這個沒有失去約束?

編譯器錯誤:

(3:22) The type 'Test.IResult<TValue,TToken>' cannot be used as type parameter 'TResult' in the generic type or method 'Test.ITokenizer<TResult,TValue>'. 
There is no implicit reference conversion from 'Test.IResult<TValue,TToken>' to 
'Test.IResult<TValue,Test.ITokenizer<Test.IResult<TValue,TToken>,TValue>>'. 
(10:22) The type 'Test.ITokenizer<TResult,TValue>' cannot be used as type parameter 'TToken' in the generic type or method 'Test.IResult<TValue,TToken>'. 
There is no implicit reference conversion from 'Test.ITokenizer<TResult,TValue>' to 
'Test.ITokenizer<Test.IResult<TValue,Test.ITokenizer<TResult,TValue>>,TValue>'. 
+0

2件事:請將編譯錯誤添加到您的文章中,以便我們知道它是什麼,其次可能告訴我們您正在嘗試做什麼,因此我們知道您爲什麼選擇此解決方案。可能有更好的解決方案,你會得到更多的想法。 – CodingYoshi

+0

@CodingYoshi我不想深究這種情況,因爲我想了解爲什麼這段代碼不能編譯。我認爲這有一個潛在的基本原因,我現在不明白。 –

+0

但您有循環引用:IResult類型約束取決於ITokenizer和vica。 – Evk

回答

2

你可以嘗試多了一個類型的約束添加到兩個接口,這樣的:

public interface IResult<TValue, TToken, TResult> 
    where TToken : ITokenizer<TResult, TValue, TToken> 
    where TResult : IResult<TValue, TToken, TResult> { 
    TToken Tokenizer { get; } 
    TValue Value { get; } 
} 

public interface ITokenizer<TResult, TValue, TTokenizer> 
    where TResult : IResult<TValue, TTokenizer, TResult> 
    where TTokenizer : ITokenizer<TResult, TValue, TTokenizer> { 
    TResult Advance(); 
} 

更醜了一點,但我想會的工作你的目標是:

public class Result : IResult<string, Tokenizer, Result> 
{ 

} 

public class Tokenizer : ITokenizer<Result, string, Tokenizer> { 

} 

我覺得主要問題不是循環引用,而只是事實編譯器不能d在您的泛型類型之間進行隱式轉換,直到您有所幫助。

更新: 我認爲你的接口缺乏Tokenizer和Result之間的強關係。 IResult接口說,TToken可以是任何分詞器,我的意思是相關任何結果。所以它可以是ITokenizer<Result1>ITokenizer<Result2>等等。但是,您不能將ITokenizer<Result1>指定爲ITokenizer<Result2>(即使結果實現相同的界面) - 這是不同的類型。標記器接口也是如此。當你改變如上所述的界面時,現在很清楚TTokenTResult的標記器,同時TResultTTokenizer的結果(現在它們是兩個具體類型,而不是接口,它們之間具有強關係)。

+0

謝謝!但對我而言,無法推斷編譯器仍然不清楚...... –

+0

@NikitaSivukhin另請參閱我對這些原因的看法(我認爲它不是編譯器無法推論出來,但這些定義真的是錯誤的,不應該編譯)。 – Evk

0

更新 由於Evk的回答否定了此答案,請不用回答這個問題。但是,我仍然在這裏留下這個答案,因爲如果別人認爲它與循環引用有關,它將有助於解釋它顯然沒有。

問題是當編譯器試圖編譯第一個接口時,它需要編譯第二個接口但編譯第二個接口,它需要編譯第一個接口。因此,它不能這樣做,因爲它不能得出結論。爲了讓事情變得更簡單,這段代碼將得到同樣的錯誤和你:

public interface IFirst<TFirst> 
    where TFirst : ISecond<IFirst<TFirst>> 
{ 

} 

public interface ISecond<TSecond> 
    where TSecond : IFirst<ISecond<TSecond>> 
{ } 

但是代碼,因爲沒有循環引用,編譯器可以得出結論如下不會得到錯誤:

public interface IFirst<TFirst> 
    where TFirst : ISecond<IFirst<TFirst>> 
{ 

} 

public interface ISecond<TSecond> 
    //where TSecond : IFirst<ISecond<TSecond>> 
{ } 
+0

看起來像@Evk反駁你的參考循環參考:-) –

+1

他完全做到了。我正試圖圍住它。 – CodingYoshi