通用类型互相引用

时间:2016-11-19 15:29:00

标签: c# generics generic-constraints

我编写简单的解析器,并希望实现接下来的两个接口:

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

2 个答案:

答案 0 :(得分:4)

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

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可以是任何标记器,我的意思是与任何结果有关。所以它可以是ITokenizer<Result1>ITokenizer<Result2>等等。但是你不能将ITokenizer<Result1>分配给ITokenizer<Result2>(即使结果实现相同的接口) - 这是不同的类型。对于tokenizer接口也是如此。当您更改上面的界面时,现在很清楚TTokenTResult的标记化器,同时TResultTTokenizer的结果(现在这两个是具体的)类型,而不是接口,它们之间有很强的关系。)

答案 1 :(得分: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>>
{ }