2013-04-03 4 views
3

Пожалуйста, обратите внимание на код ниже:VB.NET - Неправильные переменные экземпляра?

Public Class A 
    Public person1 As Person 
End Class 

Public Class B 
    Inherits A 

    Public Function CheckGender() As Boolean 
     If person1._Gender = "M" Then 
      CheckGender = True 
     End If 
    End Function 

End Class 

Public Class C 
    Inherits B 
    Public Function CheckAge() As Boolean 
     If person1._Age > 30 Then 
      CheckAge = True 
     End If 
    End Function 

End Class 

Public Class D 
    Inherits C 

    Public Sub SomeMethod() 
     Dim list As List(Of Person) = New List(Of Person) 
     Dim p1 As New Person("Ian", "M", 31) 
     Dim p2 As New Person("Susan", "F", 20) 
     Dim p3 As New Person("Mark", "M", 22) 
     list.Add(p1) 
     list.Add(p2) 
     list.Add(p3) 

     For Each Person As Person In list 
      person1 = Person 
      If CheckAge() And CheckGender() Then 
       'Do something 
      End If 
     Next 
    End Sub 

    Public Shared Sub Main() 
     Dim d As New D 
     d.SomeMethod() 
    End Sub 

End Class 

Public Class Person 
    Public _Name As String 
    Public _Gender As String 
    Public _Age As String 
    Public Sub New(ByVal Name As String, ByVal Gender As String, ByVal Age As Integer) 
     _Name = Name 
     _Gender = Gender 
     _Age = Age 
    End Sub 
End Class 

c.SomeMethod петлю через три людей и делает две проверки: b.CheckGender и c.CheckAge. CheckGender и CheckAge использовать переменную экземпляра из суперкласса A.

Код в живой среде составляет около 100 000 записей ежедневно в базе данных и удаляет те, где CheckGender и CheckAge оба являются истинными. Является ли неправильным выбором дизайна для использования переменных экземпляра в этом сценарии? Меня всегда учили использовать локальные переменные. Я ожидал бы, что объект Person будет передан CheckGender и CheckAge на каждый цикл. Или это действительно не имеет значения?

Обратите внимание, что приведенный выше код является гипотетическим примером. CheckGender и CheckAge являются сложными функциями в реальном приложении.

ответ

2

Пока CheckGender и CheckAge не имеют доступа любой частный, защищенный или внутренний элемент классов в иерархии, и общественные функции, и их логика одинакова для любого экземпляра, будучи A, B или C, его это лучший дизайн, чтобы иметь эти методы в другом классе. Сделайте их статичными, если это возможно. Вы можете заставить их принять наиболее общую реализацию ваших классов (например, A), которая позволяет проверять возраст или пол. Взятый из вашего кода, вы можете даже передать свойство Person вместо использования каких-либо классов A, B и C.

Использование наследования в вышеуказанном случае и такой логике допускается, хотя, до тех пор, как вам нужно сделать любой из следующих действий:

  • Соответствует конкретный интерфейс или базовый класс, что A, B и C классы должны реализовать/расширить, и этот интерфейс или базовый класс предоставляет методы CheckGender и CheckAge. Это может быть единственным решением, если вы передадите свои объекты стороннему API, который принимает базовый класс/интерфейс в качестве аргумента и внутренне вызывает методы проверки.

Вот пример C#:

public static class CheckUtil 
{ 
    public static bool CheckAgeOfPerson(Person p) 
    { 
     return p.Age > 30; 
    } 
    public static bool CheckAgeOfObject(A obj) 
    { 
     // NOTE: obj.person1 must be accessible - by being either public or internal. 
     // If this class is in another assembly, internal is not useful 
     return CheckAgeOfPerson(obj.person1); 
    } 
} 

A objA = ...; 
B objB = ...; 
C objC = ...; 

CheckUtil.CheckAgeOfObject(objA); 
CheckUtil.CheckAgeOfObject(objB); 
CheckUtil.CheckAgeOfObject(objC); 

CheckUtil.CheckAgeOfPerson(objA.person1); 
CheckUtil.CheckAgeOfPerson(objB.person1); 
CheckUtil.CheckAgeOfPerson(objC.person1); 
  • Обеспечить конкретную реализацию классам. Если вам нужна некоторая логика в CheckAge для экземпляров A, но либо совершенно другая проверка для экземпляров B, либо комбинация существующей и некоторой новой логики в C, тогда наследование - ваш друг. Тем не менее, если это так, я бы предпочел разоблачить интерфейс CheckGender и CheckAge и вызвать их через интерфейс. Таким образом, наследование является допустимым, но не обязательным, поскольку интерфейс удовлетворен.

вот пример в C#:

public interface IGenderCheckable 
{ 
    bool CheckGender(); 
} 

public interface IAgeCheckable 
{ 
    bool CheckAge(); 
} 

public class A : IAgeCheckable, IGenderCheckable 
{ 
    public virtual bool CheckGender() 
    { 
     return this.person1.Gender.Equals("M"); 
    } 

    public virtual bool CheckAge() 
    { 
     return this.person1.Age > 30; 
    } 
} 

public class B : A 
{ 
    public override bool CheckAge() 
    { 
      // combine custom logic with new logic 
      return this.person1.Age < 0 || base.CheckAge(); 
    } 
} 

Для сложных сценариях сочетание обоих подходов могут быть также использованы (конечно, для более сложных случаев, чем возрастные и гендерные проверки):

public class A : IAgeCheckable, IGenderCheckable 
{ 
    ... 
} 

public static class CheckUtil 
{ 
    public static bool CheckAge(IAgeCheckable obj) 
    { 
     return obj.CheckAge(); 
    } 
    public static bool CheckGender(IGenderCheckable) 
    { 
     return obj.CheckGender(); 
    } 
} 

Об использовании переменных экземпляра против локальных переменных - есть drawbac k в производительности использования переменных экземпляра в .NET, особенно когда они являются типами значений. Например, использование локального пользователя, которое равно int _someIntMember, переводится в this._someIntMember, что, в свою очередь, вызывает кучу, чтобы получить объект this, а затем обращается к его члену _someIntMember. Имея член как локальную переменную, помещает ее значение в стек и читает ее оттуда без ненужного округления через кучу. Более того, стек быстрее, чем куча.

Однако я не могу сказать, злоупотребляет ли чрезмерное использование кучи, а также злоупотреблением локальными переменными, когда они используются слишком много. Это зависит от требуемой производительности и сложности кода. Иногда локальные переменные делают код более читаемым, но если его слишком много, вы можете легко забыть, что такое каждый (и это может быть более серьезной проблемой, чем небрежное повышение производительности). Так что это вопрос стиля и необходимости.

+0

Спасибо. +1. Я подумаю об этом. Можете ли вы ответить на исходный вопрос. Неправильно ли использовать переменные экземпляра в цикле или это не имеет большого значения? Пожалуйста, не думайте, что я предлагаю, чтобы вы не ответили на мой вопрос (оставьте свой первоначальный ответ тактично). – w0051977

+0

Вы единственный ответчик на этот вопрос. Я не понимаю ваш последний комментарий. Сожалею. – w0051977

+0

Извините, я подумал об этом: http://stackoverflow.com/questions/15772573/inheritance-variable-initialisation-in-subclass#comment22460230_15772573. Я слишком устал сегодня, я думаю ... –

2

В случае, если вы заинтересованы в «фиксации» свой код, чтобы сделать человека в собственности, а не поле, изменить реализацию класса А следующим образом:

Public Class A 
    Public Property Person1 As Person 

    Public Overridable Function ComputeAge() As Integer 
     Return Person1.Age 
    End Function 

End Class 

Преимущество здесь у вас есть возможность добавить дополнительные абстракции по получению и установке этого свойства по дороге, если вам нужно. Компилятор создаст скрытое закрытое поле поддержки для свойства auto. Вы все равно будете получать доступ к Person1 из любых реализующих классов:

Public Class B 
    Inherits A 

    Public Overrides Function ComputeAge() As Integer 
     Return MyBase.Person1.Age.AddYears(1) 
    End Function 
End Class 
+0

+1. Свойства также хорошие и плохие. Хорошо то, что они обычно вставляются компилятором как часть оптимизации - и это автоматически создает местных жителей. Плохо, если свойство не является вашим кодом, и вы не знаете, что он на самом деле делает. Даже получатель свойства может выполнить непредсказуемую логику в своей реализации, которая заставит вас задуматься, где узкое место (например, объектная сущность NHibernate для ленивого свойства). До тех пор, пока это не проблема, и нет риска для нее, это хорошая практика. –

+0

@Jim Wooley, спасибо. Считаете ли вы, что человек должен быть переменной экземпляра A или вы считаете, что он должен быть передан как параметр CheckAge и CheckGender. Или это не имеет большого значения? +1. – w0051977

Смежные вопросы