Quale di questi sarebbe il modo migliore per implementare la copia di oggetti in c #?

2

Vorrei poter copiare un oggetto e modificare solo alcuni valori. Ho trovato due modi diversi per farlo.

// OPTION A - create a copy constructor and use property
//            initialization to change values


class SomeObject {
    public string PropertyA { get;set; }
    public string PropertyB { get;set; }

    public SomeObject() { }

    public SomeObject(SomeObject copyFrom) {
        PropertyA = copyFrom.PropertyA;
        PropertyB = copyFrom.PropertyB;
    }
}

var original = new SomeObject() {
    PropertyA = "original value",
    PropertyB = "some other original value",
}
// replace only the values I want to replace
var partialCopy = new SomeObject(original) {
    PropertyA = "some new value different from original",
};


// OPTION B - create an Option<T> type and use that

// used as constructor paramaters
public class Option<T> {

    // used in the case where you want
    // the default value of a type instead
    // of the original value (see usage below)
    public static readonly Default = new Option<T>(default(T));

    public T Value { get; private set; }

    public Option(T value) {
        Value = value;
    }

    public static implicit operator T(Option<T> o) {
        return o.Value;
    }

    public static implicit operator Option<T>(T value) {
        return new Option<T>(value);
    }
}

public class SomeObject {
    public string PropertyA { get; set; }
    public string PropertyB { get; set; }

    public SomeObject() { }

    public SomeObject(SomeObject toCopy,
        Option<string> propertyA = null,
        Option<string> propertyB = null)
    {
        PropertyA = propertyA ?? toCopy.PropertyA;
        PropertyB = propertyB ?? toCopy.PropertyB;
    }
}

var original = new SomeObject() {
    PropertyA = "original value",
    PropertyB = "some other original value",
};
// replace only the values I want to replace
var partialCopy = new SomeObject(original,
    propertyA: "new value");

La prima implementazione ha molto meno codice da mantenere, ma non è possibile applicare le cose nel costruttore. Il secondo è un po 'più complicato a causa della classe Option<T> ma è possibile, in effetti, applicare meglio le regole in un costruttore durante la creazione dell'oggetto. Pensi che la sicurezza del costruttore di OPTION B prevale sulla complessità, rendendola la scelta migliore. O dovrei usare il OPTION A più semplice a causa della facilità di manutenzione?

Questi sono oggetti di trasferimento dati semplici. In genere hanno molte proprietà. Sto cercando di evitare di creare oggetti come template, templateB, templateB.2, etc. quando posso semplicemente copiare l'oggetto corrente e modificare le proprietà di cui ho bisogno. Ad esempio, le proprietà A, B e C devono essere impostate su questo valore quando passa una determinata regola. Tuttavia, nel processo, le proprietà D, E e F avranno bisogno di valori diversi in base ad altre regole. Sto cercando di evitare codice come il seguente:

  // I'm trying to avoid this
  ResponseObject SomeMethod(bool inputA, bool inputB) {
     var myObject = CreateObjectFromTemplateA(); // has same values for A,B,C
     if(inputA) {
         myObject.D = "some value";
         myObject.E = "some value";
         myObject.F = "some value";
     } else if (inputB) {
         myObject.D = "some other value";
         myObject.E = "some other value";
         myObject.F = "some other value";
     }
     return myObject;
  }

  // and do something like this instead
  ResponseObject SomeMethod(bool inputA, bool inputB) {
      var defaultValue = CreateFromTemplateA()
      return inputA ? new ResponseObject(defaultValue) { D = "some value", 
                                                         E = "some value", 
                                                         F = "some value",},
           : inputB ? new ResponseObject(defaultValue) { D = "some other value",
                                                         E = "some other value",
                                                         F = "some other value",},
                    : defaultValue;
  }

  // in some cases it's as simple as one property change
  // and here is where the first method of property initialization
  // breaks down
  ResponseObject SomeMethod(bool inputA, bool inputB) {
      return new ResponseObject(CreateFromTemplateA()) {
             D = inputA ? "some value"
               : inputB ? "some other value
                        : CreateFromTemplateA(), // a second object created
      };
  }

  // here is where the constructor initialization helps
  ResponseObject SomeMethod(bool inputA, bool inputB) {
      return new ResponseObject(CreateFromTemplateA(), // with this method CreateFromTemplateA() could be a property instead of a method call
          d: inputA ? "someValue"
           : inputB ? "someValue"
                    : null // null here indicates use the property value from CreateFromTemplateA()
      );
  }
    
posta Charles Lambert 07.10.2014 - 23:03
fonte

3 risposte

1

Lo farei tramite Interfaccia fluida / concatenamento di metodi :

class Person {

  [...]

  public Person WithFirstName( String FirstName ){
    this.FirstName=FirstName;
  }

  public Person WithLastName( String LastName ){
    this.LastName=LastName;
  }

  [...]

  public Person Clone(){

    Person clone = new Person()
                        .WithFirstName(this.FirstName)
                        .WithLastName(this.LastName)
                        [...]
    return clone;
  }

}

Quindi se hai bisogno di un oggetto con valori diversi, lo farebbe con:

Person personB=personA.Clone()
                      .WithFirstName("John");

Ciò rende la tua intenzione chiara :

1) Invia un messaggio a una persona -Oggetto: clona te stesso

2) invii un nuovo messaggio al clone , che desideri con un nome diverso .

    
risposta data 10.10.2014 - 00:28
fonte
0
class SomeObject {
    public string PropertyA { get;set; }
    public string PropertyB { get;set; }

    public SomeObject WithABC(string value) {
        var clone = MemberwiseClone();
        clone.PropertyA = value;
        clone.PropertyB = value;
        clone.PropertyC = value;
        return this;
    }
}

var copy = original.WithABC("another value");
    
risposta data 08.10.2014 - 14:20
fonte
0

Ci sono diversi modi per raggiungere questo obiettivo.

Come menzionato negli altri commenti, devi considerare se l'oggetto è un semplice oggetto di proprietà o ha riferimenti di proprietà ad altri oggetti, e chi "possiede" o "gestisce" quei riferimenti o oggetti dell'oggetto.

Esempio di oggetto semplice:

public class PersonClass
{
  public string FirstName;
  public string LastName;
  public DateTime BirthDate;
  public bool IsMarried;

  public PersonClass GetSimpleCopy()
  {
    PersonClass Result = new PersonClass();
      Result.FirstName = this.FirstName;
      // assign other properties
    return Result;
  }
} // class PersonClass

Verifica che questo sia un modo molto semplice per duplicare un oggetto.

Si noti che ignoro i costruttori di "copia".

Esempio di oggetto con riferimenti ad altri oggetti, che sono gestiti ("creati" / "distrutti") dall'oggetto principale:

public class PersonClass
{
  public string FirstName;
  public string LastName;
  public DateTime BirthDate;
  public bool IsMarried;

  public PersonClass GetSimpleCopy() { ... }
} // class PersonClass

public class ItemClass
{
  public string ProductID;
  public string ProductName;
  public Float Price;
  public int Qty;

  public ItemClass GetSimpleCopy() { ... }
} // class ItemClass

public class SaleClass
{
  protected bool _Referenced;
  public float Total;

  public PersonClass Customer;
  public List<ItemClass> Items;

  public bool Referenced()
  {
    bool Result = false;
      Result = this._Referenced;
    return Result;
  } // bool Referenced()

  public ItemClass GetSimpleCopy()
  {
    ItemClass Result = new ItemClass();
      Result._Referenced = false;
      Result.Total = this.Total;
      // explicitly return "empty"
      Result.Customer = null;
      Result.Items= new List<ItemClass>();
    return Result;
  } // ItemClass GetSimpleCopy()

  public ItemClass GetReferencedCopy()
  {
    ItemClass Result = new ItemClass();
      Result._Referenced = true;
      Result.Total = this.Total;
      // explicitly return references
      Result.Customer = this.Customer;
      Result.Items = this.Items;
    return Result;
  } // ItemClass GetReferencedCopy()

  public ItemClass GetComplexCopy()
  {
    ItemClass Result = new ItemClass();
      Result._Referenced = false;
      Result.Total = this.Total;
      // explicitly make a copy of subobjects
      Result.Customer = this.Customer.GetSimpleCopy();
      // copy each item
      Result.Items = this.Items.GetSimpleCopy();
    return Result;
  } // ItemClass GetComplexCopy() 
} // class SaleClass

Ricorda che O.O. Linguaggi di programmazione che supportano i riferimenti agli oggetti, è preferibile copiare il riferimento anziché l'intero oggetto.

Tuttavia, ci sono casi in cui è richiesta la copia completa. Nel caso di oggetti complessi o composti, è meglio fornire un flag di proprietà che indica se i sottooggetti sono solo riferimenti o sono gestiti dall'oggetto principale.

A volte, un oggetto principale può gestire alcuni sottooggetti, ma avere anche un riferimento ad altri oggetti che potrebbero non essere richiesti per essere gestiti o da copiare. In questi casi, è utile un flag per ciascun sottoboject.

Si noti che la funzione "Riferimenti" indica se un oggetto composto / complesso gestisce i sottoprogetti e, i diversi metodi di copia indicano quale tipo di copia è stato applicato.

Si noti che, salta intenzionalmente i costruttori e utilizzo metodi specifici con un identificatore più chiaro.

Saluti.

    
risposta data 08.10.2014 - 00:43
fonte

Leggi altre domande sui tag