Pensez-vous que la & # 8220; implémentation automatique de l'interface & # 8221; serait utile dans .NET / C # [fermé]

StackOverflow https://stackoverflow.com/questions/656564

Question

Considérez ceci:

public class interface Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

Et ceci:

public class StubPerson : IPerson
{
    int ID { get { return 0; protected set { } }
    string FirstName { get { return "Test" } set { } }
    string LastName { get { return "User" } set { } }
    string FullName { get { return FirstName + " " + LastName; } }
}

Utilisation:

IPerson iperson = new Person();

Ou:

IPerson ipersonStub = new StubPerson();

Ou:

IPerson ipersonMock = mocks.CreateMock<IPerson>();

Nous déclarons donc en même temps l'interface IPerson et la classe Personne simultanément:

public class interface Person : IPerson

Pensez-vous qu'il serait utile de disposer de ce type de support dans .NET / C #?

Modifier:

En raison de la confusion généralisée, je pense devoir clarifier l'objectif proposé:

Sans cette fonctionnalité, vous devriez écrire:

interface IPerson
{
  int ID { get; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get; }
}

ainsi que ceci:

public class Person : IPerson
{
  int ID { get; protected set; }
  string FirstName { get; set; }
  string LastName { get; set; }
  string FullName { get { return FirstName + " " + LastName; } }
}

Je ne propose aucun changement sémantique.

Était-ce utile?

La solution

J'ai envisagé la Même chose il y a quelque temps , en particulier pour les cas où vous n'avez qu'une seule implémentation de production d'une interface, mais que vous souhaitez simuler à des fins de test. Pour le moment, cela ressemble un peu aux fichiers .c / .h de jadis.

En fin de compte, je soupçonne que la complexité supplémentaire du langage et de la lecture du code l’emporte sur ses avantages. Je serais quand même intéressé de le voir exploré plus en profondeur cependant. Même dans ce cas, il reste d’autres choses bien plus importantes sur ma liste de priorités: mieux prendre en charge l’immuabilité en haut:)

Autres conseils

Laissez-moi voir si je comprends ce que vous demandez:

Pourquoi ne pouvons-nous pas déclarer une interface:

interface IPerson
{
    string Name {get;set;}
    int ID {get;set;}
}

Et les classes qui implémentent cette interface hériteront de ses propriétés sans avoir à les déclarer à nouveau:

class Person : IPerson { } 
//person now has properties Name and ID

Si vous ne pouvez pas y parvenir, même si le texte de votre code d'interface et celui de votre classe sont très similaires, ils signifient très des choses très différentes. L’interface indique simplement que "l’implémenteur aura une chaîne Name avec getter et setter". C'est la classe qui dit "retourner un champ privé lorsque l'invité getter for name est appelé". Même si vous utilisez le raccourci auto-property pour laisser le compilateur implémenter cette logique, c'est toujours la logique , qui appartient à la classe. Juste parce que:

string Name {get;set;}

semble identique dans une interface et dans une classe, cela ne signifie même pas la même chose à distance.

Il serait très dangereux pour le compilateur de mettre en œuvre une logique arbitraire pour remplir vos contrats à votre place, au lieu de se plaindre au moment de la compilation que vous ne les avez pas mises en œuvre. Cela pourrait introduire des bugs très difficiles à détecter. Faire en sorte que les compilateurs retombent sur le comportement par défaut lorsqu'aucun comportement n'est défini est une très, très mauvaise idée.

Je pense qu'Eiffel fait quelque chose comme ceci sur .NET, afin de prendre en charge l'héritage multiple. Une déclaration de classe produit automatiquement une interface correspondante. Lorsqu'il est fait référence au type de classe, le compilateur émet généralement une référence au type d'interface. La principale exception concerne bien sûr les expressions du constructeur.

Eh bien, je pense que les autres réponses vous aideront à comprendre l'utilisation de l'interface pour la logique abstraite dans différentes classes concrètes. Je pense également que vous pouvez accomplir quelque chose de similaire à ce que vous voulez en utilisant les outils de refactoring intégrés à VS.

Définissez votre classe ...

public class Person
{
  public int ID { get; protected set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string FullName { get { return FirstName + " " + LastName; } }
}

Cliquez ensuite avec le bouton droit de la souris et sélectionnez Refactor - > Extraire l'interface.

Ceci créera un fichier séparé contenant l’interface pour la définition de la classe, vous pourrez alors modeler l’interface et implémenter les classes en conséquence.

Interface extraite:

interface IPerson
{
    string FirstName { get; set; }
    string FullName { get; }
    int ID { get; }
    string LastName { get; set; }
}

Je suppose que je passe à côté de l'essentiel - que faites-vous en mélangeant une classe et une interface? Quel problème résolvez-vous avec cette approche?

Ceci:

IPerson iperson = new Person();

est déjà légal en C #.

Modifier: Pour plus de précision, le code ci-dessus est légal, compte tenu des éléments suivants:

interface IPerson { }

class Person : IPerson { }

J'aimerais au moins que Visual Studio mette en œuvre mes propriétés à partir d'une interface si je le demande.

Malheureusement, cette option ne fonctionne pas et je dois m'occuper des talons d'exception non mis en oeuvre

Non, car vous seriez obligé d'exposer tous les membres publics d'une interface. Essayez ReSharper et ne vous inquiétez plus jamais à ce sujet.

Resharper peut fournir cette fonctionnalité, par exemple,

  1. Vous pouvez d'abord écrire votre classe Person.
  2. Vous pouvez extraire votre interface en en tirant des membres sur le site. Interface IPerson.

Par conséquent, Visual Studio peut générer automatiquement des stubs d'implémentation pour vous.

MISE À JOUR

Quoi qu'il en soit, commençons par expliquer les interfaces en citant le code que vous avez fourni dans votre question:

public class interface Person : IPerson
{
    int ID { get; protected set; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string FullName { get { return FirstName + " " + LastName; } }
}

Vous devez comprendre qu'une interface n'est pas une classe abstraite. Une interface est simplement un contrat , un schéma directeur, ce qui signifie qu’elle dira à un objet ce à quoi il doit s’attendre dans un autre objet sans se soucier de la façon dont il est mis en œuvre.

Une classe abstraite, en revanche, peut contenir des fragments de fonctionnalité pouvant être hérités et remplacés.

Dans le cas ci-dessus, votre " interface " est invalide car:

  • vous ne pouvez pas déclarer de contraintes de portée sur les interfaces (publiques, privées, protégées, internes) car il s'agit d'un détail d'implémentation
  • vous ne pouviez pas déclarer une implémentation par défaut (votre propriété FullName , par exemple), car il s'agit à nouveau d'un détail d'implémentation

Il me semble que vous voulez vraiment une classe abstraite, par exemple,

public abstract class BasePerson
{
    public abstract int ID { get; protected set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual string FullName { get { return FirstName + " " + LastName; } } 
}

Je ne fais que deviner, mais c'est peut-être ce dont vous avez vraiment besoin.

UPDATE 2

D'accord, je pense que je veux en venir à ce que vous voulez, alors vous voulez pouvoir écrire ceci:

public interface IPerson
{
    int ID { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
    string FullName { get; }
}

Et pour votre implémentation, il suffit d'écrire ceci:

public class Person : IPerson
{
    public int ID { get; protected set; }
    public string FullName { get { return FirstName + " " + LastName; } } 
}

Sans avoir besoin de spécifier les propriétés Prénom et Nom.

Le premier problème que nous devons résoudre est le fait que les interfaces n'autorisent pas les délimiteurs d'accès dans son implémentation: il en résulterait que les propriétés hériteraient du délimiteur d'accès par défaut, qui est privé.

Deuxièmement, bien que, à nos yeux, , chaîne FirstName {get; ensemble; } dans une interface et chaîne publique FirstName {get; ensemble; } dans une classe sont les mêmes, ils ne sont en réalité pas:

  • dans une interface, la définition de la propriété indiquera que les signatures de méthode pour les méthodes getter et / ou setter seront disponibles pour toutes les classes implémentant cette interface.
  • dans une classe, la définition de la propriété demandera au CLR de créer un objet anonyme qui contiendra la valeur de ladite propriété.

Différence subtile pour le programmeur, un monde à part pour le compilateur.

Enfin, lorsque vous indiquez que vous implémentez une interface, Visual Studio effectue une magie synctactique qui crée automatiquement ces talons de propriété pour vous.

Je pense qu'une meilleure abstraction pour cela est un trait, ou, comme je l'ai décrit ici , un rôle. C'est comme une interface avec du code. Votre exemple pourrait être codé comme ceci:

public role RPerson { 
  int ID { get; protected set; } 
  string FirstName { get; set; } 
  string LastName { get; set; } 
  string FullName { get { return FirstName + " " + LastName; } } 
} 

public class Person : RPerson { }

public class StubPerson : RPerson { 
    int ID { get { return 0; protected set { } } 
    string FirstName { get { return "Test" } set { } } 
    string LastName { get { return "User" } set { } } 
    string FullName { get { return FirstName + " " + LastName; } } 
} 

// ...

RPerson rperson = new Person(); 

RPerson rpersonStub = new StubPerson(); 
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top