-1
public void Test<T>(T reference, T same, T differentOrGreater)
{
    var comparisonInterface = typeof(T)
        .GetInterfaces()
        .FirstOrDefault(
            i => i.IsGenericType &&
            i.GetGenericTypeDefinition() == typeof(IComparisonOperators<,,>));

    this.GetType()
        .GetMethod(nameof(this.CheckComparisonOperators),
            BindingFlags.NonPublic | BindingFlags.Instance)!
        .MakeGenericMethod(typeof(T))
        .Invoke(this, [reference, same, differentOrGreater]);
}

private void CheckComparisonOperators<T>(T reference, T same, T greater)
    where T : IComparisonOperators<T, T, bool>
{
     // do stuff
}

public record BaseSize<T>(uint Value) : IComparisonOperators<T, BaseSize<T>, bool>
    where T : BaseSize<T>
{
    // implementation
}

public record DerivedSize(uint Value) : BaseSize<DerivedSize>(Value);

The call works fine with BaseSize but fails for DerivedSize:

Test(new DerivedSize(1), new DerivedSize(1), new DerivedSize(2));

how can I fix this?

2
  • "The call works fine with BaseSize" Of what? BaseSize<T> needs a type parameter, can you show a working example? Commented 2 days ago
  • Could you share the exact exception message and stack trace you get for DerivedSize? Also, which .NET version and target framework are you using (for example net7.0, net8.0)? Commented 2 days ago

2 Answers 2

1

Derived does not implement IComparisonOperators<Derived, Derived, bool>, it implements IComparisonOperators<Derived, BaseSize<Derived>, bool> (i.e. you can do public record DerivedSize1(uint Value) : BaseSize<DerivedSize>(Value);).

One workaround can be changing CheckComparisonOperators accordingly:

private void CheckComparisonOperators<T>(T reference, T same, T greater)
    where T : BaseSize<T>, IComparisonOperators<T, BaseSize<T>, bool>
{
    // do stuff
}

Or implement the interface directly:

public record DerivedSize(uint Value) : BaseSize<DerivedSize>(Value), IComparisonOperators<DerivedSize> { /*...*/ }

Another variant is to make BaseSize<T> implementing IComparisonOperators<T, T, bool> (I assume that this is what you actually want) and use explicit interfaces to implement it:

public record BaseSize<T>(uint Value) : IComparisonOperators<T, T, bool>
    where T : BaseSize<T>
{
    static bool IEqualityOperators<T, T, bool>.operator ==(T? left, T? right)
    {
        throw new NotImplementedException();
    }

    static bool IEqualityOperators<T, T, bool>.operator !=(T? left, T? right)
    {
        throw new NotImplementedException();
    }

    static bool IComparisonOperators<T, T, bool>.operator <(T left, T right)
    {
        throw new NotImplementedException();
    }

    static bool IComparisonOperators<T, T, bool>.operator >(T left, T right)
    {
        throw new NotImplementedException();
    }

    static bool IComparisonOperators<T, T, bool>.operator <=(T left, T right)
    {
        throw new NotImplementedException();
    }

    static bool IComparisonOperators<T, T, bool>.operator >=(T left, T right)
    {
        throw new NotImplementedException();
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

I cannot change the method CheckComparisonOperators to have further contraints since it is a generic test method which must work on all objects, that is why i query the capabilities via reflection. DerivedSize is a derivation just for testing the base class, so it makes no sense to implement it there because then I would run the test against test code.
Then you can use later option. Or change the signature to be of multiple generic types. To suggest the best option I'm afraid I'll need a bit more info.
but with this I cannot use the operators for the derived classes: bool result = new DerivedSize(1) > new DerivedSize(2);
that is already covered in my answer, see the explicit interface implementation part
that is already covered in my answer, see the explicit interface implementation part
I'm afraid that those are the only options you have, at least for the provided code. Can you please share the CheckComparisonOperators implementation?
1

The interface is declared as

public interface IComparisonOperators<TSelf, TOther, TResult>
    : IEqualityOperators<TSelf, TOther, TResult>
where TSelf : IComparisonOperators<TSelf, TOther, TResult>?

Therefore, you must swap the parameters, so that T same comes before T reference. Also, the constraint says where T : BaseSize<T>, therefore declaring a parameter as BaseSize<T> would make it effectively BaseSize<BaseSize<T>> because T is BaseSize<T> already according the the constraint.

Change your method to

private void CheckComparisonOperators<T>(T same, T reference, T greater)
    where T : IComparisonOperators<T, T, bool>
{
    // do stuff
}

and the record to

public record BaseSize<T>(uint Value) : IComparisonOperators<T, T, bool>
    where T : BaseSize<T>
{
    // implementation
}

And also change the order of the same and reference parameters in the Test<T> method (at two places).

See fiddle: https://dotnetfiddle.net/TBSt9y

(I realize that some T same, T reference parameters are still reversed in the fiddle, but since they are of the same type, the test does compile and run).

3 Comments

Have you tried making public record BaseSize<T>(uint Value) : IComparisonOperators<T, T, bool> compiling? It fails for me with " Program.cs(91, 33): [CS0563] One of the parameters of a binary operator must be the containing type"
Have you added the constraint where T : BaseSize<T>? It compiles and runs for me.
Yes, though I've used autogenerated implementation. Can you please share full fiddle?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.