2015-04-23 12 views
13

Mam następujący kod:Modyfikacja pola wielokrotnego strunowe

class SearchCriteria 
    { 
     public string Name { get; set; } 
     public string Email { get; set; } 
     public string Company { get; set; } 
     // ... around 20 fields follow 

     public void Trim() 
     { 
      if(! String.IsNullOrEmpty(Name)) 
      { 
       Name = Name.Trim(); 
      } 

      if(! String.IsNullOrEmpty(Email)) 
      { 
       Email = Email.Trim(); 
      } 

      // ... repeat for all 20 fields in the class. 
     } 
    } 

Chcę napisać jedną funkcję, która będzie odpowiednio przyciąć pól, coś takiego:

public void Trim() 
{ 
    Trim(Name); 
    Trim(Email); 
    // ... 
} 

private static void Trim(ref string field) 
{ 
    if(! String.IsNullOrEmpty(field)) 
    { 
     field = field.Trim(); 
    } 
} 

Oczywiście, to nie jest dozwolone w języku C#. Jedną z opcji, którą mam, jest napisanie pomocnika i użycie odbicia. Czy istnieje inny sposób, w jaki mogę to osiągnąć (zastanowienie się nad tak wieloma właściwościami będzie miało zdecydowanie lepsze wyniki w tym konkretnym scenariuszu i nie stać mnie na to)?

+7

Może można przyciąć struny w swoich ustawieniach nieruchomości? –

+2

Prawdopodobnie przeprojektuję całą klasę tak, aby używała HashMap do zapisywania 20 różnych wartości String, ponieważ wydają się one Listą Opcjonalnych atrybutów. Następnie masz jedną metodę PUT, która może wykonać przycinanie. – Falco

+0

@Falco: skąd wiadomo, że wszystkie są ciągami? Może być "DateTime DayOfBirth". Używanie 'List ' byłoby zbyt abstrakcyjne, ponieważ nie możesz powiedzieć: daj mi 'SearchCriteria.Email' już. A jeśli użyjesz 'Dictionary ' musisz znać klucz. –

Odpowiedz

11

Jeśli masz już kod, o co pytasz? Jest czytelny i wydajny. Ale może lepiej byłoby, gdyby te właściwości najpierw zmniejszyły przekazaną wartość.

class SearchCriteria 
{ 
    private string _Name; 
    public string Name 
    { 
     get { return _Name; } 
     set { _Name = value == null ? null : value.Trim(); } 
    } 

    private string _Email; 
    public string Email 
    { 
     get { return _Email; } 
     set { _Email = value == null ? null : value.Trim(); } 

    } 

    private string _Company; 
    public string Company 
    { 
     get { return _Company; } 
     set { _Company = value == null ? null : value.Trim(); } 

    } 

    // ... around 20 fields follow 
} 

Nawet jeśli można zastosować podejście refleksyjne. Uważaj, że ten kod jest zawsze trudny do zrozumienia i utrzymania. I będzie cicho przycinać właściwości, nawet jeśli nie powinny być przycinane. Na przykład, jeśli inny programista rozszerza tę klasę.

6
public void Trim() 
{ 
    Name = Trim(Name); 
    Email = Trim(Email); 
    // ... 
} 

private string Trim(string field) 
{ 
    if(! String.IsNullOrEmpty(field)) 
     field = field.Trim(); 
    return field; 
} 

EDIT:

Spróbuj również zastosować Trim fuction w ustawiaczy właściwości

class SearchCriteria 
{  
    private string Trim(string field) 
    { 
     if(! String.IsNullOrEmpty(field)) 
      field = field.Trim(); 
     return field; 
    } 

    private string _name; 
    public string Name 
    { 
     get { return _name; } 
     set { _name = Trim(value); } 
    } 

    private string _email; 
    public string Email 
    { 
     get { return _email; } 
     set { _email = Trim(value); } 

    } 

    // ... other string properties 
    // no public void Trim() method 
} 
3

Wydaje przesada .. zapisany czas w Trim(), zmarnowany czas w deklaracji pola

class SearchCriteria 
{ 
    private Dictionary<string, string> _internalValues = new Dictionary<string, string>(); 
    public string Name { get { return _internalValues.ContainsKey("Name") ? _internalValues["Name"] : null; } set { _internalValues["Name"] = value; } } 
    .... 

    public void Trim() 
    { 
     foreach (var entry in _internalValues) 
     { 
      if (!string.IsNullOrEmpty(entry.Value)) _internalValues[entry.Key] = entry.Value.Trim(); 
     } 
    } 
} 
+0

Jeśli nie ma żadnych starodawnych powodów, dla których interfejs publiczny klasy musi pozostać taki sam, zerwałbym wszystkich akcesorów i po prostu wykonałbym SearchCriteria.get (SCEnum.Email); – Falco

2

Możesz zmienić swój kod w następujący sposób

public void Trim() 
{ 
    Name = Trim(Name); 
    Email = Trim(Email); 
    // ... 
} 

private static void Trim(string field) 
{ 
    if(! String.IsNullOrWhiteSpace(field)) 
    { 
     field = field.Trim(); 
    } 

    return field; 
} 

Nie można przekazać właściwość przez odniesienie, a metoda String.IsNullOrEmpty() rozważy white-space jako niepusty, więc użyłem String.IsNullOrWhiteSpace().

2

Po zakończeniu. Nie wiem, czy to dobry sposób. Ale możesz użyć, jak powiedział Tim Schmelter. Ale, jak również wskazał, kod jest trudniejszy do utrzymania, a jeśli ktoś go rozszerzy, może to być problem. Ale tutaj jest przykładem, jak można to zrobić:

class SearchCriteria 
{ 
    public string Name { get; set; } 
    public string Email { get; set; } 
    public string Company { get; set; } 
    // ... around 20 fields follow 

    public void Trim() 
    { 
     typeof(SearchCriteria).GetProperties() 
      .Where (w =>w.PropertyType==typeof(string)) 
      .ToList().ForEach(f=> 
      { 
       var value=f.GetValue(this); 
       if(value!=null && !string.IsNullOrEmpty(value.ToString())) 
       { 
        f.SetValue(this,value.ToString().Trim(),null); 
       } 
      }); 
    } 
} 
3

wolę ten styl, powielanie jest nieuniknione, aby zachować czytelność, ale będzie to zaoszczędzić trochę miejsca na ekranie

class SearchCriteria 
{ 
    public string Name { get; set; } 
    public string Email { get; set; } 
    public string Company { get; set; } 


    public void Trim() 
    { 
     if(!String.IsNullOrEmpty(Name)) Name = Name.Trim(); 
     if(!String.IsNullOrEmpty(Email)) Email = Email.Trim(); 
     if(!String.IsNullOrEmpty(Company)) Company = Company.Trim(); 
    } 
} 

void Main() 
{ 
    var criteria = new SearchCriteria(); 
    criteria.Email = "thing "; 
    Console.WriteLine(criteria.Email.Length); 
    criteria.Trim(); 
    Console.WriteLine(criteria.Email); 
    Console.WriteLine(criteria.Email.Length); 
} 
2

Odbicie będzie oczywiście nie jest to rozwiązanie najbardziej wydajne, ale z pewnymi modyfikacjami i buforowaniem nadal może być używane.

Oto pomocnik odbicie, które pozwalają na tworzenie kolekcji delegatów mutator i buforować go wewnątrz klasy:

public static class ReflectionHelper 
{ 
    public static IEnumerable<PropertyInfo> GetPropertiesOfType<THolder, TPropType>() 
    { 
     return typeof(THolder).GetPropertiesOfType(typeof(TPropType)); 
    } 


    public static IEnumerable<PropertyInfo> GetPropertiesOfType(this Type holderType, Type propType) 
    { 
     if (holderType == null) 
      throw new ArgumentNullException("holderType"); 
     if (propType == null) 
      throw new ArgumentNullException("propType"); 

     return holderType 
      .GetProperties() 
      .Where(prop => 
       prop.PropertyType == propType); 
    } 


    public static IEnumerable<Action<Func<TPropType, TPropType>>> CreateMutators<THolder, TPropType>(THolder holder) 
    { 
     if (holder == null) 
      throw new ArgumentNullException("holder"); 

     return holder.GetType() 
      .GetPropertiesOfType(typeof(TPropType)) 
      .Select(prop => 
       new 
       { 
        getDelegate = (Func<TPropType>)Func.CreateDelegate(
         typeof(Func<TPropType>), 
         holder, 
         prop.GetGetMethod()), 
        setDelegate = (Action<TPropType>)Action.CreateDelegate(
         typeof(Action<TPropType>), 
         holder, 
         prop.GetSetMethod()) 
       }) 
      .Select(accessor => 
       (Action<Func<TPropType, TPropType>>)((mutate) => 
       { 
        var original = accessor.getDelegate(); 
        var mutated = mutate(original); 
        accessor.setDelegate(mutated); 
       })) 
      .ToArray(); 
    } 
} 

Kod klasy - ty buforować mutatorów i używać ich wewnątrz metody przycinania:

class SearchCriteria 
{ 
    public SearchCriteria() 
    { 
     this.Name = "adsfasd  "; 
     this.Email = "  adsfasd  "; 
     this.Company = " asdf adsfasd  "; 

     this.stringMutators = ReflectionHelper.CreateMutators<SearchCriteria, String>(this); 
    } 

    public string Name { get; set; } 
    public string Email { get; set; } 
    public string Company { get; set; } 
    // ... around 20 fields follow 

    private IEnumerable<Action<Func<String, String>>> stringMutators; 


    private String TrimMutate(String value) 
    { 
     if (String.IsNullOrEmpty(value)) 
      return value; 

     return value.Trim(); 
    } 

    public void Trim() 
    { 
     foreach (var mutator in this.stringMutators) 
     { 
      mutator(this.TrimMutate); 
     } 
    } 

    public override string ToString() 
    { 
     return String.Format("Name = |{0}|, Email = |{1}|, Company = |{2}|", 
      this.Name, 
      this.Email, 
      this.Company); 
    } 
} 

Kod Główny:

  var criteria = new SearchCriteria(); 

      Console.WriteLine("Before trim:"); 
      Console.WriteLine(criteria); 


      Console.WriteLine("After trim:"); 
      criteria.Trim(); 
      Console.WriteLine(criteria); 

P.S .: Jednak nie jest to rozwiązanie bardzo bezpośrednie lub jasne, więc polecam iść z "inteligentnym" ustawiaczem (pobierającym), jak to opisano w innych odpowiedziach. Lub, być może, możesz wypróbować jakieś Aspect Oriented Programming approach.

3

Jeśli nie korzystałeś z automatycznych właściwości, możesz użyć ref. Zgadzam się, że nie jest to optymalne.

class SearchCriteria 
{ 
    private string _name; 
    public string Name { get { return _name; } set { _name = value; }} 
    public string Email { get; set; } 
    public string Company { get; set; } 
    // ... around 20 fields follow 

    void Trim(ref string str) 
    { 
     if (!String.IsNullOrEmpty(str)) 
     { 
      str = str.Trim(); 
     } 
    } 

    public void Trim() 
    { 
     Trim(ref _name); 

     // ... repeat for all 20 fields in the class. 
    } 
}