4

Say we want to define a class that allows basic arithmatic, called 'Addable'. Addable things can be added.

abstract class Addable
{
   public abstract Addable Add(Addable X, Addable Y)
}

What is the correct way to implement an Addable? The following doesn't work, it gives:

Number does not implement inherited abstract member Addable.Add(Addable, Addable).

class Number : Addable
{
     public int Value;

     public Number(int Val)
     {
        Value = Val;
     }

     public Number Add(Number X, Number Y)
     {
         return new Number(X.Value + Y.Value);
     }
}

I think the problem is that Add takes (Number,Number) as its arguements which aren't generic enough, but I do not know how to proceed.

Edit: As some people have requested to know what this is to be used for, let me elaborate. I am using an algorithm that relies on taking the maximum of several objects. Depending on the use case, these objects are numbers, or distributions. To stay with the example above I will pretend I need to add these numbers or distributions. So I want to have code that looks something like:

Addable LongAlgorithm(Addable X, Other parameters)
{
   ... // Lots of code that may contain X
   Z = Add(X,Y)
   ... // Code Using Z.

   return Answer // Answer is of the same type as X in the input.
}

Edit 2: With the feedback given this question seems to be drifting into the realm of "Interface vs Base class". Perhaps others who read this question might find that question illuminating.

I hope the question is clear, I am new to S.O. and although I have tried to stick to the guidelines as much as possible, I will be happy to modify the question to make it clearer.

  • 1
    The `Add` method needs to take `Addable` objects, not `Number` objects. – DavidG Feb 06 '19 at 12:28
  • 1
    Add must return an Addable and take 2 addables. – Prodigle Feb 06 '19 at 12:28
  • 2
    You won't escape the need to filter out the types somewhere. With `is` and `as` etc. There is no good solution which makes this a not-so-good idea. – H H Feb 06 '19 at 12:29
  • 4
    I suggest you create an interface `IAddable` with a `T Add(T x, T y)` method then have `Number` implement `IAddable`. You could add a type parameter to your `Addable` class if you need to use inheritence. – Lee Feb 06 '19 at 12:29
  • 1
    You need to implement _"public override Addable Add(Addable X, Addable Y)"_ to remove the error - but an interface would be better as Lee has suggested. – PaulF Feb 06 '19 at 12:30
  • You could make your abstract class generic: (e.g. `Addable`), but like Henk says, this is probably a bad idea. – DavidG Feb 06 '19 at 12:31
  • 1
    It seems like you're better off using a generic here rather than a class hierarchy – Prodigle Feb 06 '19 at 12:35
  • @DavidG - why would it (`it` being the generic abstract class, I assume) be a bad idea? – Rand Random Feb 06 '19 at 12:52
  • @HenkHolterman - could you explain why? – Rand Random Feb 06 '19 at 12:53
  • For a start, your interface doesn't really make sense. I would be happier if the `Add` method had a single parameter and return the addition of that parameter and the instance on which is was called. Or if this method was static and took 2 parameters. – DavidG Feb 06 '19 at 14:47

3 Answers3

8

It all depends on why you want that Addable base class, and how it will be used. It's worth updating your question to explain this. Here's one possibility, which might not meet your use-case:

public interface IAddable<T>
{
    T Add(T x, T y);
}

public class Number : IAddable<Number>
{
    public int Value { get; set; }

    public Number(int value)
    {
        Value = value;
    }

    public Number Add(Number other)
    {
        return new Number(Value + other.Value);
    }
}

You could of course use an abstract base class as well here, if there was a need:

public abstract class Addable<T>
{
    public abstract T Add(T x, T y);
}

If you want to make sure that types can only do class Foo : IAddable<Foo> and not class Foo : IAddable<Bar>, then you can add a generic type restriction:

public interface IAddable<T> where T : IAddable<T>
{
    T Add(T x, T y);
}

In response to your edit:

Use my types from above and do this:

T LongAlgorithm<T>(T x, Other parameters) where T : IAddable<T>
{
   ... // Lots of code that may contain x
   T z = x.Add(y);
   ... // Code Using z

   return z;
}

Note that I've changed your Add method so that it's an instance method, which adds itself to another instance.

If you wanted to keep the signature as Add(x, y), you probably want something like this:

public class Number
{
    public int Value { get; set; }
    public Number(int value)
    {
        Value = value;
    }
}

public interface IAdder<T>
{
    T Add(T x, T y);
}

public class NumberAdder : IAdder<Number>
{
    public static readonly NumberAdder Instance = new NumberAdder();
    private NumberAdder() { }
    public Number Add(Number x, Number y)
    {
        return new Number(x.Value + y.Value);
    }
}

T LongAlgorithm<T>(T x, IAdder<T> adder, Other parameters)
{
   ... // Lots of code that may contain x
   T z = adder.Add(x, y);
   ... // Code Using z

   return z;
}

Then call it like

Number z = LongAlgorithm(new Number(3), NumberAdder.Instance, ...);
canton7
  • 37,633
  • 3
  • 64
  • 77
  • Although this would be a way to go, if OP wants to use abstract class (due to "reasons"), I think it's good to show example with abstract generic class as well – Darjan Bogdan Feb 06 '19 at 12:37
2

You can do it:

abstract class Addable<T>
{
   public abstract T Add(T X, T Y);
}

class Number : Addable<Number>
{
     public int Value;

     public Number(int Val)
     {
        Value = Val;
     }

     public override Number Add(Number X, Number Y) 
     {
         return new Number(X.Value + Y.Value);
     }
}
1
class Number : Addable
{
     public int Value;

     public Number(int Val)
     {
        Value = Val;
     }

     public Number Add(Number X, Number Y)
     {
         return new Number(X.Value + Y.Value);
     }
}

needs to be

class Number : Addable
{
     public int Value;

     public Number(int Val)
     {
        Value = Val;
     }

     public override Number Add(Addable X, Addable Y)
     {
         return new Number(X.Value + Y.Value);
     }
}

When you declare an abstract method, you need to implement it exactly as it was declared, otherwise it will not compile. But dont worry, because your Number class derives from Addable, you can still call the method without casts.


EDIT:

Looking back at the code, there's a lot that can be improved here.

For instance, you're exposing a variable (Value) - this is against the laws of encapsulation (OOP). I'd suggest using a property in this case. Also, as has been mentioned in the comments (I overlooked this fact), the property would have to go in your base class.

Speaking of the base class, in this case it would be wise to turn this in to an interface, as you're not implementing anything, only declaring.

Use an interface when you want to expose the blueprint of something. Use an abstract class when you want to expose the frame of something.

Essentially you can think of an interface like the building plan (blueprint) of a house. It declares the structure of said house. Think of an abstract class as the frame of the house. It's not quite finished yet, but you can generally stand inside the walls and will be dry - AKA it exposes some functionality already.

Here's an idea of how to solve your problem. It's not the most elegant, but it should give you a general idea.

In fact, after writing this down, a much more elegent solution came to mind: using structs.

public interface IAddable {

    /// <summary >
    /// This is the value we want.
    /// </summary>
    int Value { get; set; }

    IAddable Add(IAddable x, IAddable y);

}

public class Number : IAddable {

    public Number() { }

    public Number(int num) => Value = num;

    public int Value { get; set; }

    public IAddable Add(IAddable x, IAddable y) {
        return (Number)(x.Value + y.Value);
    }

    public static implicit operator Number(int num) => new Number(num);
    public static implicit operator Number(double num) => new Number((int)num);
    public static implicit operator int(Number num) => num.Value;
}

EDIT 2

As others have mentioned in their answers, you could also use generics to account for more datatypes. Implicit operators (as used in my example) are not necessarily the best option to use, especially if you're still just beginning to learn the language.

SimonC
  • 1,547
  • 1
  • 19
  • 43
  • 2
    This won't compile since `Addable` doesn't have public `Value` property, and return type of method should be `Addable` – Darjan Bogdan Feb 06 '19 at 12:34
  • 1
    @DarjanBogdan Just saw that myself, I'm editing the answer as we speak. – SimonC Feb 06 '19 at 12:36
  • 1
    Having a struct mutable struct can be a bit dangerous and counter-intuitive: if you're going down the struct route, make sure that it's immutable. Also, point out that it will be boxed if you ever pass it to a method which takes an `IAddable`, for instance. – canton7 Feb 06 '19 at 13:10