【问题标题】:Generic types references each other泛型类型相互引用
【发布时间】:2016-11-19 15:29:00
【问题描述】:

我写了一个简单的解析器,想实现接下来的两个接口:

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: next token 和 next tokenizer。所以,我想在Result 类中存储标记和标记器,并为此添加编译时约束。

现在我在构建这两个接口时遇到了编译时错误。

我认为下一个类可以实现具有所有约束的接口:

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

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

谁能解释一下怎么回事?也许为什么不可能或如何使这段代码正确?

附:对于我的任务,我可以简单地使用IResult&lt;TValue, TToken&gt; 接口而没有任何约束,但我可以在不失去约束的情况下实现它吗?

编译器错误:

(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>'.

【问题讨论】:

  • 两件事:请将编译错误添加到您的帖子中,以便我们知道它是什么,其次也许告诉我们您正在尝试做什么,以便我们知道您选择此解决方案的原因。可能有更好的解决方案,你会得到更多的想法。
  • @CodingYoshi 我不想深入了解这种情况,因为我想了解为什么这段代码无法编译。我认为有一个潜在的基本原因,我现在不明白。
  • 但是你有循环引用:IResult 类型约束取决于 ITokenizer,反之亦然。
  • 错误很明显,它表示无法将 IResult 转换为 IResult, TValue>>。我不知道你还想知道什么?
  • @Evk 但是为什么像public interface Circular&lt;T&gt; where T : Circular&lt;T&gt; 这样的简单循环引用是允许的并且有效?

标签: c# generics generic-constraints


【解决方案1】:

您可以尝试向两个接口添加一个类型约束,如下所示:

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

}

我认为主要问题不是循环引用,而是编译器无法推断出泛型类型之间的隐式转换,除非您提供一点帮助。

更新: 我认为您的界面在 Tokenizer 和 Result 之间缺乏强关系。 IResult 接口说 TToken 可以是 any 分词器,我的意思是与 any Result 相关。所以它可以是ITokenizer&lt;Result1&gt;ITokenizer&lt;Result2&gt;等等。但是您不能将ITokenizer&lt;Result1&gt; 分配给ITokenizer&lt;Result2&gt;(即使结果实现了相同的接口)——这是不同的类型。标记器接口也是如此。当您像上面那样更改接口时,现在很明显TTokenTResult 的分词器,同时TResultTTokenizer 的结果(现在这是两个具体类型,不是接口,具有很强的关系之间)。

【讨论】:

  • @NikitaSivukhin 也请参阅我对原因的看法(我认为不是编译器无法推断,而是那些定义确实是错误的,不应该编译)。
【解决方案2】:

更新 请忽略这个答案,因为 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>>
{ }

【讨论】:

  • 看起来@Evk 反驳了你关于循环引用的论点:-)
  • 他完全做到了。我正试图绕过它。
猜你喜欢
  • 2018-10-20
  • 2021-11-11
  • 1970-01-01
  • 2023-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多