2009-05-27 2 views
8

Я работаю на C#, и я начинаю играть со свойствами. Одна вещь, которую я не знаю, - это лучший способ/где поставить логику для набора аксессуаров свойств класса и способы обработки ошибок.Свойства C# - Задайте вопрос

Например, у меня есть это (основной) класс:

class Person 
{ 
    private int _Age = 18; 

    public Person() 
    { 

    } 

    public int Age 
    { 
     get 
     { 
      return _Age; 
     } 
     set 
     { 
      _Age = value; 
     } 
    } 
} 

Теперь у меня есть требование о собственности Age, 0 < < Возраст 100. Где поставить логику для этого?

Должен ли я положить его в собственность?

public int Age 
    { 
     get 
     { 
      return _Age; 
     } 
     set 
     { 
      if (value < 0 || value > 99) 
       // handle error 
      else 
       _Age = Convert.ToInt32(value); 
     } 
    } 

или через класс, который создает объект Person?

static void Main(string[] args) 
{ 
    Person him = new Person(); 
    int NewAge = -10; 

    if (NewAge < 0 || NewAge > 100) 
     // handle error 
    else 
     him.Age = NewAge; 
} 

Теперь, если проблема с NewAge (она не соответствует моему ограничению)? Должен ли я создать настраиваемое исключение и выбросить его? Должен ли я просто печатать сообщение о том, что срок службы действителен?

Я сделал некоторый поиск в Google и не могу найти ничего, что полностью отвечает на мои вопросы. Мне нужна книга: -/

+3

Заметим также, что ваш сеттер не нуждается в 'Convert.ToInt32 (значение)' ... потому что свойство является 'int',' value' также будет 'int'. – jerryjvl

+0

Разве это не считается защитным программированием? Или я принимаю это далеко? –

+0

Ты забираешь это слишком далеко. Нет никакой возможности, что вещь, которую вы получаете, не является int. –

ответ

23

Используйте свойство setter, оно есть по этой причине (добавление функциональности в поле).

Если значение вне допустимого диапазона передано, вы можете выбросить ArgumentOutOfRangeException или просто установить минимальное (или максимальное) значение, но это зависит от вашего требования к процессу.

+3

+1 за предложение бросить ArgumentOutOfRangeException, а не только Exception –

+0

Я бы добавил немного нюансов к этому, сказав, что средство настройки свойств, как правило, является подходящим местом ..., но ТОЛЬКО для вещей, которые вы абсолютно уверены, должно быть истинным во все времена , Проверки, которые могут иметь исключения (помиловать каламбуры), скорее всего, должны перейти на метод проверки, чтобы пользователь класса мог определить, подходит ли ошибка. Основная причина этого заключается в том, что в общем случае небезосновательно допускать возраст более 99 или 100 (который по первому вопросу не позволяет по какой-либо причине) – jerryjvl

+0

Я бы: 1) четко документировал поведение и требования это свойство (так что пользователь знает, как его использовать) и 2) поместить ясное сообщение об исключении, вызванном 3) это создаст чистый код (не усеянный методами проверки, которые запутают будущих разработчиков) –

3

Поместите это в собственность. Это одна из главных целей свойств!

Рассмотрите, что вы будете возлагать ответственность на нижний уровень. В приведенном примере не требуется ничего, кроме значения , значение, чтобы принять его решение. Он даже не зависит от других членов того же класса. Нет причин для остальной части класса знать, насколько действительность работает для свойства Age, и, конечно, нет причин для того, чтобы какой-либо другой код знал об этом.

6

Вам нужно будет поместить логику в сеттер и выбросить исключение, если оно не соответствует требованию. Тем не менее, вы также захотите создать статический метод IsValidAge или что-то еще, чтобы классы, создающие Person, могли проверять возрастное значение, а не просто видеть, генерирует ли это исключение. В качестве альтернативы вы могли бы иметь свойства MinAge и MaxAge, чтобы код вызова мог проверить, находится ли возраст, который они собираются установить, между ним.

Не создавайте собственный тип исключения, используйте ArgumentOutOfRangeException или что-то в этом роде.

3

Возможно, вы захотите изучить интерфейс IDataErrorInfo.

Внедряя этот интерфейс в свой класс, вы откроете класс для других механизмов, которые могут извлечь дополнительную дополнительную информацию об ошибке.

0

Используйте setter и используйте ArgumentOutOfRangeException, как уже говорили другие.

Иногда вы вызываете другой метод, как правило, что-то вроде OnAgeChanged (int age), где вы можете выполнить проверку (которая может быть вызовом еще одного метода, чтобы вы могли ее использовать в любом месте) и вызывать обработчик событий, который если он подключен, может применяться другая логика в зависимости от того, как вы используете свой объект. Возможно, это не обязательно в вашем сценарии, но это довольно часто для свойств. Особенно, если вы собираетесь обновить форму - форма будет подключаться к событию AgeChanged для обновления самой.

1

Я собираюсь сыграть адвоката дьявола и дать ответ, почему вы НЕ хотите вставлять его в сеттера. Есть много случаев, когда вы хотите установить значение своего свойства Age и позже спросите, действителен ли этот объект в целостном смысле.

Например, сохранить свое имущество просто:

public int Age { get; set; } 

Затем, когда недопустимое значение передается в вас может иметь некоторую IsValid функцию, которая указывает, если рассматриваемый объект нормально. Это может быть чрезвычайно полезно, потому что вы можете выполнять более сложную проверку, отличную от простого ограничения по возрасту.

bool IsValid() 
{ 
    if (Age < 0 || Age > 99) 
    return false; 
} 

Для чего-то простого, как это есть не много пользы, но учтите также, что вы можете использовать это в вашей настойчивости слоя, так что вы можете гарантировать, что любой объект, который не является действительным никогда не будут сохранены. В тех случаях вы не обязательно хотите исключить исключение.

Также рассмотреть это:

DateTime StartDate { get; set; } 
DateTime EndDate { get; set;} 

bool IsValid() 
{ 
    return StartDate > EndDate  
} 

Это только псевдокод, но вы получите мою точку зрения. Это то, что вы не можете сделать внутри сеттера или, по крайней мере, не таким образом, чтобы его можно было поддерживать.

+0

Это интересный момент зрения. Хотя я согласен с тем, что наличие отдельной функции IsValid может оказаться полезным, я не понимаю, почему мне разрешено создавать условие, когда сама модель может быть недействительной. Поймал бы ошибку как можно раньше, уменьшит головные боли в будущем? Что происходит, когда код сохранения забыл вызвать IsValid? Это одна важная вещь, которую нужно помнить. –

11

Я бы реализовать это следующим образом:

public int Age 
{ 
    get 
    { 
     return _Age; 
    } 
    set 
    { 
     if (IsValidAge(value)) 
      _Age = value; 
     else 
      throw new ArgumentOutOfRangeException("value", string.Format("value should be between {0} and {1} inclusive.", MinAge, MaxAge)); 
    } 
} 

private bool IsValidAge(int age) 
{ 
    return (age >= MinAge && age <= MaxAge); 
} 

Несколько вещей, чтобы отметить:

  • Не изменяйте их значение вместо того, чтобы выбросить исключение, это неожиданное поведение.
  • .NET framework выбрасывает исключения Argument * в сеттерах, поэтому я бы сказал, что это хорошая идея следовать этой практике. В этом случае ArgumentOutOfRangeException является идеальным, ИМО.
  • При обращении к аргументу в сообщениях об исключениях и документах xml стандарт должен вызывать аргумент «значение», а не имя вашего свойства.
  • Я бы рекомендовал MinAge и MaxAge как частные consts в вашем классе, не попадайте в ловушку сообщений об ошибках жесткого кодирования с границами границ в них, нет ничего хуже, чем сказать «5 в недействительности, введите число между 1 и 10 ", когда кто-то меняет спецификацию позже, но забывает обновлять строку.
+0

В этом ответе немало хороших моментов. –

+0

Я согласен с реализацией вроде этого. Вы должны обязательно выполнить проверку на множестве, и это должен быть внешний вызов. Я также рекомендую переместить его куда-нибудь более доступным, если это то, что вы, возможно, захотите использовать в другом месте. – JonBWalsh

0

Если вы делаете факторизованный дизайн (то есть важно, чтобы состояние объекта было действительным во всех точках жизни объекта), тогда вы можете установить и проверить это в конструкторе. В дизайне компонента вы хотите проверить это в методе, который использует свойство, например.

class Person 
{ 
    public int calculateTimeToExpiration() 
    { 
    if (Age < 0 || Age > 100) 
    //throw 
    } 
} 

Static void main является клиентским кодом и не является хорошим местом для бизнес-логики.

0

Спасибо за ответы! В случае, если вам было любопытно, вот что я могу изменить свой маленький пример (хотя теперь он более подробно) (на основе комментариев).

class Person 
{ 
    private string _FirstName = "Joe"; 
    private string _LastName = "Smith"; 
    private int _Age = 18; 

    private const int MinAge = 1; 
    private const int MaxAge = 99; 

    public Person() 
    { 

    } 

    public Person(string FirstName, string LastName, int Age) 
    { 
     this.FirstName = FirstName; 
     this.LastName = LastName; 
     this.Age = Age; 
    } 

    public string FirstName 
    { 
     get 
     { 
      return _FirstName; 
     } 
     set 
     { 
      _FirstName = value; 
     } 
    } 

    public string LastName 
    { 
     get 
     { 
      return _LastName; 
     } 
     set 
     { 
      _LastName = value; 
     } 
    } 

    public int Age 
    { 
     get 
     { 
      return _Age; 
     } 
     set 
     { 
      if (IsValidAge(value)) 
       throw new ArgumentOutOfRangeException("value","Please enter a positive age less than 100."); 
      else 
       _Age = value; 
     } 
    } 

    private bool IsValidAge(int age) 
    { 
     return (age < MinAge || age > MaxAge); 
    } 

    public override string ToString() 
    { 
     if (Age == 1) 
      return "My name is " + FirstName + " " + LastName + " and I am " + Age + " year old."; 
     else 
      return "My name is " + FirstName + " " + LastName + " and I am " + Age + " years old."; 
    } 
} 

static void Main(string[] args) 
    { 
     Person him, her; 

     try 
     { 
      him = new Person("Joe Bob", "McGruff", 1); 
      Console.WriteLine(him); 
     } 
     catch (ArgumentOutOfRangeException range) 
     { 
      Console.WriteLine(range.Message); 
     } 

     try 
     { 
      her = new Person(); 
      her.Age = -5; 
      Console.WriteLine(her); 
     } 
     catch (ArgumentOutOfRangeException range) 
     { 
      Console.Write(range.Message); 
     } 

     Console.ReadKey(); 
    } 
+0

Я бы изменил логику в методе IsValidAge. Вы еще раз вызываете метод «IsValid», вы возвращаете true для недействительных. Кроме того, не нужно преобразовывать. ToInt в ваш сеттер. Значение будет автоматически int. Помните, что свойства - это просто синтаксический сахар для методов. Под капотом он настраивается на частную void set_Age (int x) {..}, где x -> значение. Таким образом, нет НИКАКОГО ПУТЯ для значения NOT не быть int. – BFree

0

Обновленное ответ хорош, но вы должны очистить один маленький кусочек в вашей сеттер/проверки

public int Age 
    { 
     get 
     { 
      return _Age; 
     } 
     set 
     { 
      if (!IsValidAge(value)) 
       throw new ArgumentOutOfRangeException("Age","Please enter a positive age less than 100."); 

      _Age = value; 
     } 
    } 

    private bool IsValidAge(int age) 
    { 
     return (age > MinAge && age < MaxAge); 
    }