泛型类型相互引用
Generic types references each other
我编写了简单的解析器并想实现接下来的两个接口:
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
是一个不可变的 class,用于按标记拆分字符串。我们可以调用 Advance
方法并获取 Result
:下一个标记和下一个分词器。所以,我想在 Result
class 中存储令牌和分词器,并想为此添加编译时约束。
现在我在构造这两个接口时遇到编译时错误。
我认为接下来 classes 可以实现具有所有约束的接口:
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>'.
您可以尝试向两个接口添加一个类型约束,如下所示:
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 tokenizer,我的意思是与 any 结果相关。所以可以是ITokenizer<Result1>
、ITokenizer<Result2>
等。但是您不能将 ITokenizer<Result1>
分配给 ITokenizer<Result2>
(即使结果实现相同的接口)——这是不同的类型。分词器接口也是如此。当你像上面那样更改接口时,现在很清楚 TToken
是 TResult
的分词器,同时 TResult
是 TTokenizer
的结果(现在这是两个具体的类型,而不是接口,它们之间有很强的关系)。
更新
请忽略这个答案,因为 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>>
{ }
我编写了简单的解析器并想实现接下来的两个接口:
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
是一个不可变的 class,用于按标记拆分字符串。我们可以调用 Advance
方法并获取 Result
:下一个标记和下一个分词器。所以,我想在 Result
class 中存储令牌和分词器,并想为此添加编译时约束。
现在我在构造这两个接口时遇到编译时错误。
我认为接下来 classes 可以实现具有所有约束的接口:
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>'.
您可以尝试向两个接口添加一个类型约束,如下所示:
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 tokenizer,我的意思是与 any 结果相关。所以可以是ITokenizer<Result1>
、ITokenizer<Result2>
等。但是您不能将 ITokenizer<Result1>
分配给 ITokenizer<Result2>
(即使结果实现相同的接口)——这是不同的类型。分词器接口也是如此。当你像上面那样更改接口时,现在很清楚 TToken
是 TResult
的分词器,同时 TResult
是 TTokenizer
的结果(现在这是两个具体的类型,而不是接口,它们之间有很强的关系)。
更新 请忽略这个答案,因为 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>>
{ }