Equivalent Java generic circular reference C#

Viewed 83

I am porting a Java library in C#, and I found this weird class that uses a generics circular reference:

public static class UserAgentAnalyzerDirectBuilder<UAA extends UserAgentAnalyzerDirect, B extends UserAgentAnalyzerDirectBuilder<UAA, B>> 
{
   // Implementation
}

I can't well understand how a circular reference can work, but as I see is indeed possible.

That code can be easily translated to C#:

public class UserAgentAnalyzerDirectBuilder<UAA, B> where UAA: UserAgentAnalyzerDirect where B: UserAgentAnalyzerDirectBuilder<UAA, B>
{
  // Implementation
}

Assuming my equivalent class is correct, I need to implement the method that instantiate that weird class.

In Java I have this code:

public static UserAgentAnalyzerDirectBuilder<? extends UserAgentAnalyzer, ? extends UserAgentAnalyzerDirectBuilder<?, ?>> newBuilder() {
    return new UserAgentAnalyzerDirectBuilder<>(new UserAgentAnalyzer());
}

In C# I tried to reproduce with:

public static UserAgentAnalyzerDirectBuilder<UAA, UserAgentAnalyzerDirectBuilder<UAA, B>> newBuilder<UAA, B>() 
            where UAA: UserAgentAnalyzerDirect
            where B: UserAgentAnalyzerDirectBuilder<UAA, B>
        {
            return new UserAgentAnalyzerDirectBuilder<UAA, UserAgentAnalyzerDirectBuilder<UAA, B>> (new UserAgentAnalyzer());
        }

But it doesn't work. I am wondering if I did something wrong or if in C# generics circular references are indeed possible.

1 Answers

What you see is not a circular reference. The type parameter constraint just lets you pass in a type which is a descendant of the generic type specified by the constraint.

The following code example compiles and I think will do what you need:

public class UserAgentAnalyzerDirect { }

public class UserAgentAnalyzerDirectBuilder<UAA, B> 
    where UAA : UserAgentAnalyzerDirect 
    where B : UserAgentAnalyzerDirectBuilder<UAA, B>
{
    // this method is supposed to implement the effect of the 
    // constructor in the original Java code
    public void SetUAA(UAA a) { }

    // further implementation
}

public static UserAgentAnalyzerDirectBuilder<UAA, B> NewBuilder<UAA, B>()
    where UAA : UserAgentAnalyzerDirect, new()
    where B : UserAgentAnalyzerDirectBuilder<UAA, B>, new()
{
    // Unlike in Java, C# allows instantiating generic type parameters only using 
    // a parameter-less constructor. Hence we use the SetUAA method here instead.
    var a = new UAA();
    var b = new B();
    b.SetUAA(a);
    return b;
}

Then you can create custom descendants of the generic classes above like this:

public class CustomUserAgentAnalyzerDirect : UserAgentAnalyzerDirect { }

public class CustomUserAgentAnalyzerDirectBuilder : UserAgentAnalyzerDirectBuilder<CustomUserAgentAnalyzerDirect, CustomUserAgentAnalyzerDirectBuilder> { }
Related