Nikita Sivukhin Nikita Sivukhin - 16 days ago 10
C# Question

Generic types references each other

I write simple parser and want to implement next two interfaces:

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


It has next purpose:
ITokenizer
is an immutable class for splitting the string by tokens. We can call
Advance
method and get
Result
: next token and next tokenizer. So, I want store token and tokenizer in
Result
class and want add compile-time constraint for this.

Now I have a compile-time error during construct this two interfaces.

I thought that next classes can implement interfaces with all constraints:

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

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


Can anyone explain what's wrong? Maybe why it's impossible or how make this code correct?

P.S. For my task I can simply use
IResult<TValue, TToken>
interface without any constraints, but can I implement this without losing constraints?

Compiler errors:

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

Evk Evk
Answer

You can try to add one more type constraint to both interfaces, like this:

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

It is a bit more ugly, but I think will work for your goal:

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

}

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

}

I think the main problem is not circular references, but just the fact compiler cannot deduce implicit conversion between your generic types until you help it a bit.

Comments