Mark
Mark

Reputation: 71

How to avoid casts when working with generics and interfaces in C#

In the following code, IPoint and IPointGroup are abbreviated versions of existing interfaces that define how data is shared between many parts of a large application.

The abbreviated generic implementations of RefPoint<T> and PointGroup<T> are used in a new subsystem that deals with large volumes of points so consideration needs to be given to minimizing the duplication of and casting required to work with the points.

At the bottom of the code, two problems are illustrated:

  1. To work with all of the points via their common interface, the approach I've taken requires each point be cast
  2. I'm unable to cast or otherwise work with the points via the IPointGroup<T> generic interface.

I'd appreciate suggestions/improvements to the approach I've taken. I'm quite new to C# generics so I may be missing something obvious.

using System;

namespace Generic
{

public interface IPoint { }
public interface IPointGroup
{  void Add( IPoint x );
}

public interface IPointGroup<T> where T : IPoint
{  void Add( T x );
}

public class RefPoint<T> : IPoint
{
    public int X, Y;
    T _refersTo;

    public RefPoint( int x, int y, T refersTo )
    {
        X = x;
        Y = y;
        _refersTo = refersTo;
    }
    public T refersTo()
    {  return _refersTo;
    }
}

public class PointGroup<T> : IPointGroup where T : IPoint
{
    internal void Add( T y )
    {
        Console.WriteLine("In Add( T y )");
    }
    public void Add( IPoint x )
    {
        Console.WriteLine("In Add(IA x)");
        Add((T)x);
    }
}

public class Swerve : IPoint { }
public class HardBrake : IPoint { }
public class StopSign { };

class Program
{
    static void Main( string[] args )
    {
        PointGroup<Swerve> swerves = new PointGroup<Swerve>();
        PointGroup<HardBrake> hardBrakes = new PointGroup<HardBrake>();
        PointGroup<RefPoint<StopSign>> stopSigns = new PointGroup<RefPoint<StopSign>>();
        PointGroup<IPoint> all = new PointGroup<IPoint>();

        Swerve s = new Swerve();
        swerves.Add(s);

        HardBrake h = new HardBrake();
        hardBrakes.Add(h);

        StopSign ss = new StopSign();
        RefPoint<StopSign> ss_ref = new RefPoint<StopSign>(1,1,ss);
        stopSigns.Add(ss_ref);

        // Problem #1: Each element is cast
        all.Add(s);
        all.Add(h);
        all.Add(ss_ref);

        // Problem #2: Can't cast as follows
        // IPointGroup<Swerve> iSwerves = (IPointGroup<Swerve>)swerves;
        // IPointGroup<HardBrake> iHardBrakes = (IPointGroup<HardBrake>)hardBrakes;
        // IPointGroup<RefPoint<StopSign>> iStopSigns = (IPointGroup<RefPoint<StopSign>>) stopSigns;
        // IPointGroup<IPoint> iAll = (IPointGroup<IPoint>)all;
    }
}

}

Upvotes: 2

Views: 633

Answers (1)

Ondrej Tucny
Ondrej Tucny

Reputation: 27964

Here you implement IPointGroup:

public class PointGroup<T> : IPointGroup where T : IPoint

while you probably meant IPointGroup<T>:

public class PointGroup<T> : IPointGroup<T> where T : IPoint

Also, IPointGroup and IPointGroup<T> are not related anyhow in your design. The common approach when a non-generic and generic version of an interface is needed, is that the generic one inherits the non-generic one; hence:

public interface IPointGroup<T> : IPointGroup where T : IPoint

Plus to benefit from the generic version primarily, consider implementing the non-generic one explicitly, like this:

public class PointGroup<T> : IPointGroup<T> where T : IPoint
{
    // implicit implementation of IPointGroup<T>.Add(T)
    public void Add(T y) { … }

    // explicit implementation of IPointGroup.Add(IPoint)
    void IPointGroup.Add(IPoint x)
    {
        Add((T)x);
    }
}

Upvotes: 2

Related Questions