Search code examples
c#genericsinterfaceinterface-implementation

Is it possible to implement an interface for a type I can't change (in the context of C#'s preview feature: static abstract interface members)


EDIT: I've finally found out why I was remembering external interface implementations possibly being a feature, it is because months ago I must have been reading the static abstract interface members proposal at around the same time as this discussion (specifically the part under "Explicit implementation and disambiguation") and the two must have merged in my mind over time.

I've been playing around with static abstract interface members, and I was wondering if it is possible to somehow tell the compiler how a specific type implements a specific interface even though the type doesn't actually implement the interface in its declaration. That is, is it possible to implement the interface externally?

I'm asking this because I remember that when I first learned about static abstract interface members months ago, this was supposed to be one of the features I learned about, but I can't find the source of these claims again (I'm 90% sure it was a youtube video).

Has this ever been planned? Is it going to be implemented?

An example of what I mean:

public struct Point2D : 
    IAdditionOperators<Point2D, Point2D, Point2D>, 
    ISubtractionOperators<Point2D, Point2D, Point2D>
{
    public float X, Y;

    public static Point2D operator + (Point2D left, Point2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y };
    public static Point2D operator - (Point2D left, Point2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y };
    public static Point2D operator * (Point2D point, float multiplier) => new() { X = point.X * multiplier, Y = point.Y * multiplier };
}
public static TInterpolated LinearInterpolation<TInterpolated, TTime>(TInterpolated start, TInterpolated end, TTime interpolation)
    where TTime : INumber<TTime>
    where TInterpolated : 
        IAdditionOperators<TInterpolated, TInterpolated, TInterpolated>,
        ISubtractionOperators<TInterpolated, TInterpolated, TInterpolated>,
        IMultiplyOperators<TInterpolated, TTime, TInterpolated>
{
    interpolation = TTime.Clamp(interpolation, TTime.Zero, TTime.One);
    return start + (end - start) * interpolation;
}
public static class SomeClass
{
    public static Point2D SomeMethod(Point2D startingPoint, Point2D goalPoint, float time)
    {
        Point2D lerpedPoint = LinearInterpolation(startingPoint, goalPoint, time);
        return lerpedPoint;
    }
}

In SomeMethod(), there will be an error because Point2D doesn't implement IMultiplyOperators<Point2D, float, Point2D> even though it implements the operator the interface requires.

Now, say I can't change Point2D, is there a way for me to make it work by implementing the interface externally via the already existing multiply operator? Again, I remember the (probably) video saying this will be possible.


Solution

  • If the Type you have uses the partial keyword on the class definition, then you will be able to modify a class, to use an interface. But if the type is defined as immutable, and doesn't permit to DI behaviour, then the short answer is no.

    public interface ISomeInterface
        {
            int DoStuff();
        }
    
        public partial class TestClass
        {
    
        }
    
        public partial class TestClass : ISomeInterface
        {
            public int DoStuff()
            {
                throw new System.NotImplementedException();
            }
        }
    

    Or you can implement a DI interface on class to allow composed behaviour.

        public class OtherClass
        {
            private ISomeInterface _someInterface;
            public OtherClass(ISomeInterface someInterface) {
                _someInterface = someInterface;
            }
    
            public void CalculateStuff()
            {
                _someInterface.DoStuff();
            }
        }
    

    Outside of these two options, there is really no way to modify a type, you do not have control over.

    Of these two methods you should instictively use composition. But if someone else i responsible for the class, you obviously do not have this control. In the case, you should create your own solution to whatever "DoStuff()" does.

    And then use composition to allow modified behaviour, that might indeed allow the specific types behaviour to exist next to your own.