63 votes

Couramment interfaces et de l'héritage en C#

Je vais vous montrer un problème par exemple. Il y a une classe de base avec interface fluide:

class FluentPerson
{
    private string _FirstName = String.Empty;
    private string _LastName = String.Empty;

    public FluentPerson WithFirstName(string firstName)
    {
        _FirstName = firstName;
        return this;
    }

    public FluentPerson WithLastName(string lastName)
    {
        _LastName = lastName;
        return this;
    }

    public override string ToString()
    {
        return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
    }
}

et un enfant de la classe:

class FluentCustomer : FluentPerson
{
    private long _Id;

    private string _AccountNumber = String.Empty;

    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        _AccountNumber = accountNumber;
        return this;
    }
    public FluentCustomer WithId(long id)
    {
        _Id = id;
        return this;
    }

    public override string ToString()
    {
        return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
    }
}

Le problème est que lorsque vous appelez customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith") vous ne pouvez pas ajouter d' .WithId(123) à la fin parce que le type de retour de la WithLastName() méthode est FluentPerson (pas FluentCustomer).

Comment ce problème généralement résolu?

49voto

Steck Points 731

Essayez d'utiliser certains de l'Extension des méthodes.

static class FluentManager
{
    public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
    {
        person.FirstName = firstName;
        return person;
    }

    public static T WithId<T>(this T customer, long id) where T : FluentCustomer
    {
        customer.ID = id;
        return customer;
    }
}

class FluentPerson
{
    public string FirstName { private get; set; }
    public string LastName { private get; set; }

    public override string ToString()
    {
        return string.Format("First name: {0} last name: {1}", FirstName, LastName);
    }
}

class FluentCustomer : FluentPerson
{
    public long ID { private get; set; }
    public long AccountNumber { private get; set; }

    public override string ToString()
    {
        return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
    }
}

après vous pouvez l'utiliser comme

new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);

44voto

Yann Trevin Points 2747

Vous pouvez utiliser des génériques pour y parvenir.

public class FluentPerson<T>
    where T : FluentPerson<T>
{
    public T WithFirstName(string firstName)
    {
        // ...
        return (T)this;
    }

    public T WithLastName(string lastName)
    {
        // ...
        return (T)this;
    }
}

public class FluentCustomer : FluentPerson<FluentCustomer>
{
    public FluentCustomer WithAccountNumber(string accountNumber)
    {
        // ...
        return this;
    }
}

Et maintenant:

var customer = new FluentCustomer()
  .WithAccountNumber("123")
  .WithFirstName("Abc")
  .WithLastName("Def")
  .ToString();

4voto

Dzmitry Huba Points 3333

Logiquement, vous devez configurer des choses de la plus spécifique (client) à moins spécifique (une personne) ou sinon, il est même difficile de le lire, malgré la fluidité de l'interface. En suivant cette règle dans la plupart des cas, vous n'aurez pas besoin d'avoir des ennuis. Toutefois, si pour une raison quelconque vous avez encore besoin de mélange, vous pouvez utiliser intermédiaire mettant l'accent sur des énoncés comme

static class Customers
{
   public static Customer AsCustomer(this Person person)
   {
       return (Customer)person;
   }
}

customer.WIthLastName("Bob").AsCustomer().WithId(10);

3voto

RameshVel Points 24472
 public class FluentPerson
 {
    private string _FirstName = String.Empty;
    private string _LastName = String.Empty;

    public FluentPerson WithFirstName(string firstName)
    {
        _FirstName = firstName;
        return this;
    }

    public FluentPerson WithLastName(string lastName)
    {
        _LastName = lastName;
        return this;
    }

    public override string ToString()
    {
        return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
    }
}


   public class FluentCustomer 
   {
       private string _AccountNumber = String.Empty;
       private string _id = String.Empty;
       FluentPerson objPers=new FluentPerson();



       public FluentCustomer WithAccountNumber(string accountNumber)
       {
           _AccountNumber = accountNumber;
           return this;
       }

       public FluentCustomer WithId(string id)
       {
           _id = id;
           return this;
       }

       public FluentCustomer WithFirstName(string firstName)
       {
           objPers.WithFirstName(firstName);
           return this;
       }

       public FluentCustomer WithLastName(string lastName)
       {
           objPers.WithLastName(lastName);
           return this;
       }


       public override string ToString()
       {
           return objPers.ToString() + String.Format(" account number: {0}",  _AccountNumber);
       }
   }

Et d'appeler à l'aide

  var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();

3voto

richardtallent Points 17534

Est une interface fluide vraiment le meilleur appel, ici, ou serait un initialiseur de mieux?

 var p = new Person{
      LastName = "Smith",
      FirstName = "John"
      };

 var c = new Customer{
      LastName = "Smith",
      FirstName = "John",
      AccountNumber = "000",
      ID = "123"
      };

Contrairement à une interface fluide, cela fonctionne très bien sans les méthodes héritées de redonner de la classe de base et gâcher la chaîne. Lorsque vous héritez d'un bien, l'appelant à ne pas se préoccuper de savoir si FirstName a été mise en oeuvre Personne ou d'un Client ou d'un Objet.

Je trouve cela bien plus lisible, que ce soit sur une seule ligne ou plusieurs, et vous n'avez pas à passer par la peine de fournir couramment auto-décorer les fonctions qui correspondent à chaque propriété.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X