4
votes

En C #, comment implémenter une classe abstraite qui s'appelle elle-même dans une fonction, par exemple comme une classe d'objets Addable?

Disons que nous voulons définir une classe qui autorise l'arithmatique de base, appelée 'Addable'. Des éléments supplémentaires peuvent être ajoutés.

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.
}

Quelle est la bonne façon d'implémenter un addable? Ce qui suit ne fonctionne pas, ça donne:

Number n'implémente pas le membre abstrait hérité 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);
     }
}

Je pense que le problème est que Add prend (Number, Number) comme arguments qui ne sont pas assez génériques, mais je ne sais pas comment procéder. p >

Edit: Comme certaines personnes ont demandé à savoir à quoi cela doit servir, laissez-moi élaborer. J'utilise un algorithme qui repose sur la prise du maximum de plusieurs objets. Selon le cas d'utilisation, ces objets sont des nombres ou des distributions. Pour rester avec l'exemple ci-dessus, je ferai semblant d'avoir besoin d'ajouter ces nombres ou distributions. Je veux donc avoir du code qui ressemble à quelque chose comme:

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

Edit 2: Avec les commentaires donnés, cette question semble dériver dans le domaine de " Interface vs classe de base ". Peut-être que d'autres qui liront cette question trouveront cette question éclairante.

J'espère que la question est claire, je suis nouveau dans S.O. et bien que j'aie essayé de m'en tenir aux directives autant que possible, je serai heureux de modifier la question pour la rendre plus claire.


10 commentaires

La méthode Add doit prendre des objets Addable , pas des objets Number .


Add doit retourner un Addable et prendre 2 addables.


Vous n'échapperez pas à la nécessité de filtrer les types quelque part. Avec est et comme etc. Il n'y a pas de bonne solution qui en fait une mauvaise idée.


Je vous suggère de créer une interface IAddable avec une méthode T Add (T x, T y) puis de faire implémenter Number IAddable . Vous pouvez ajouter un paramètre de type à votre classe Addable si vous avez besoin d'utiliser l'héritage.


Vous devez implémenter "public override Addable Add (Addable X, Addable Y)" pour supprimer l'erreur - mais une interface serait meilleure comme Lee l'a suggéré.


Vous pouvez rendre votre classe abstraite générique: (par exemple Addable ), mais comme le dit Henk, c'est probablement une mauvaise idée.


Il semble que vous feriez mieux d'utiliser ici un générique plutôt qu'une hiérarchie de classes


@DavidG - pourquoi serait-ce ( ça étant la classe abstraite générique, je suppose) une mauvaise idée?


@HenkHolterman - pouvez-vous expliquer pourquoi?


Pour commencer, votre interface n'a pas vraiment de sens. Je serais plus heureux si la méthode Add avait un seul paramètre et renvoyait l'ajout de ce paramètre et de l'instance sur laquelle est appelée. Ou si cette méthode était statique et prenait 2 paramètres.


3 Réponses :


1
votes
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;
}

3 commentaires

Cela ne sera pas compilé car Addable n'a pas de propriété publique Value , et le type de retour de méthode doit être Addable


@DarjanBogdan Je viens de voir cela moi-même, je modifie la réponse pendant que nous parlons.


Avoir une structure struct mutable peut être un peu dangereux et contre-intuitif: si vous suivez la route struct, assurez-vous qu'elle est immuable. Faites également remarquer qu'il sera encadré si vous le passez à une méthode qui prend un IAddable , par exemple.



8
votes

Tout dépend de la raison pour laquelle vous voulez cette classe de base Addable et de la manière dont elle sera utilisée. Cela vaut la peine de mettre à jour votre question pour expliquer cela. Voici une possibilité, qui pourrait ne pas répondre à votre cas d'utilisation:

Number z = LongAlgorithm(new Number(3), NumberAdder.Instance, ...);

Vous pouvez bien sûr également utiliser une classe de base abstraite ici, s'il y avait un besoin:

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;
}

Si vous voulez vous assurer que les types ne peuvent faire que class Foo: IAddable et pas class Foo: IAddable , alors vous pouvez ajouter une restriction de type générique:

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;
}

En réponse à votre modification:

Utilisez mes types ci-dessus et procédez comme suit:

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

Notez que j'ai changé votre méthode Add afin que ce soit une méthode d'instance, qui s'ajoute à une autre instance .

Si vous vouliez conserver la signature comme Add (x, y) , vous voudrez probablement quelque chose comme ceci:

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

Puis appelez-le comme

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);
    }
}


1 commentaires

Bien que ce soit une voie à suivre, si OP veut utiliser une classe abstraite (pour des «raisons»), je pense qu'il est également bon de montrer l'exemple avec une classe générique abstraite



2
votes

Vous pouvez le faire:

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);
     }
}


0 commentaires