Разработчики C # привыкли работать с полями , свойствами и методами . Но есть определенная путаница, когда дело доходит до понимания того, как они работают в Ruby. Итак, давайте быстрое освежение C #.
Поля, свойства и методы в C #
Поля — это переменные, относящиеся либо к объекту (поля экземпляра), либо к классу (статические поля). Лучше всего, чтобы разработчики не выставляли поля публично, так как это раскрывает слишком много внутренних элементов класса. Поле экземпляра объявлено так:
public class Customer
{
private int _age;
}
Обратите внимание, что подчеркивание перед «age» — это просто общее соглашение, которому следуют несколько разработчиков на C #. Чтобы выставить такое поле, создается «свойство», которое определяется как пара методов получения и метода установки:
public class Customer
{
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
}
Свойство определяется только методом get в том случае, если оно предназначено только для «доступа», но не для «назначения». В случаях, когда геттеры и сеттеры просто получают или устанавливают значение поля, можно использовать функцию C #, которая называется auto properties :
public class Customer
{
public int Age { get; set; }
}
Когда свойство определяется таким образом, компилятор C # создает код, похожий на тот, который я показывал ранее (он создает вспомогательное поле и реализацию геттеров / сеттеров).
Теперь, как это выглядит в Ruby …?
В следующем посте кратко рассматриваются переменные экземпляра и класса: NET to Ruby: методы и переменные . Чтобы объявить переменную экземпляра, мы определяем ее в инициализаторе класса (его конструкторе ), которому предшествует @:
class Customer
def initialize
@age = 18
end
end
На этом этапе @age очень похож на приватное поле в C #, так как к нему можно получить доступ в любом месте класса, где оно определено, но не за его пределами. Чтобы получить к нему доступ или назначить его извне, мы должны определить методы доступа к нему:
class Customer
def initialize
@age = 18
end
def age
@age
end
def age=(value)
@age = value
end
end
В отличие от C #, где мы определили свойство Age, содержащее методы set и get, здесь мы создали метод age, который возвращает значение переменной экземпляра @age, а также метод age = , который принимает значение и присваивает его переменная экземпляра.
Подобно автоматическим свойствам C #, Ruby также предлагает конструкцию, позволяющую легко создавать методы доступа. Код выше можно переписать так:
class Customer
def initialize
@age = 18
end
attr_accessor :age
end
attr_accessor не является ключевым словом в Ruby; это нечто, называемое «макросом класса», который представляет собой метод метапрограммирования для добавления динамического поведения в класс. В этом случае добавляются средства чтения и записи. Мы также можем только добавить средства чтения или записи с помощью attr_reader или attr_writer, соответственно.
Accessors, которые делают немного больше …
Очень часто в C # создается свойство, в котором методы доступа могут сделать немного больше, чем просто получить и / или установить значение переменной экземпляра. Например, может быть, получателю нужно конкатанировать значения перед его возвратом:
public class Customer
{
private string _firstName;
private string _lastName;
public string FullName
{
get { return string.Format("{0} {1}", _firstName, _lastName); }
}
}
В приведенном выше примере свойство FullName имеет метод получения, который объединяет поля _firstName и _lastName. Такая практика распространена и в Ruby:
class Customer
def initialize
@first_name = "Claudio"
@last_name = "Lassala"
end
def full_name
"#{@first_name} #{@last_name}"
end
end
Имейте в виду, что круглые скобки являются необязательными в Ruby, поэтому такой метод, как full_name выше, может быть вызван так:
cust = Customer.new
puts cust.first_name
Очистка кода с помощью метапрограммирования
Довольно часто мы добавляем логические свойства в класс, чтобы мы могли спросить объект об определенных вещах. Возьмите этот пример:
public class User
{
private string _profile;
public User(string profile)
{
_profile = profile;
}
public bool IsDoctor
{
get { return _profile == "doctor"; }
}
public bool IsPatient
{
get { return _profile == "patient"; }
}
}
Теперь предположим, что существует больше типов профилей, чем просто «доктор» и «пациент», поэтому в этом случае класс User будет иметь одно свойство для каждого типа профиля. Подобный класс может быть создан в Ruby примерно так:
class User
def initialize(profile)
@profile = profile
end
def doctor?
@profile == 'doctor'
end
def patient?
@profile == 'patient'
end
end
Что с суффиксом «?» Для доктора? а пациент? методы? Часто, когда метод в Ruby предназначен для возврата логического значения, этот метод называется методом предиката , и обычной практикой является использование его с суффиксом «?». Поэтому вместо того, чтобы писать код, например «if user.is_doctor», мы пишем «if user.doctor?».
Действительно ли у класса User есть несколько методов, таких как доктор? а пациент? (по одному для каждого типа профиля), класс можно переписать, чтобы использовать метапрограммирование. Вот пример того, как это может выглядеть:
class User
def initialize(profile)
@profile = profile
end
def method_missing(method_name, *args, &block)
method_name = method_name.to_s
if method_name.ends_with?("?")
return @profile == method_name.chop
end
super if self.responds_to?(method_name)
end
end
Заметили, что оба доктора? а пациент? методы исчезли, и был добавлен метод method_missing . Теперь происходит то, что всякий раз, когда какой-либо код запрашивает объект пользователя, например, «if user.doctor?», Такой метод отсутствует в классе, и вызывается метод method_missing . В приведенном выше примере method_missing проверяет, заканчивается ли метод, вызываемый для объекта («имя_метода») символом «?», И, если это так, сравнивает @profile с ним. Если поддерживаются новые профили, нет необходимости добавлять свойства в класс User, поскольку он готов к его обработке.
Резюме
Важно понимать тот факт, что в Ruby на самом деле нет «свойств» (есть методы), и их можно создавать автоматически (используя макросы класса attr_accessor, attr_reader, attr_writer) или вручную (просто определяя методы ), поскольку эти вещи используются довольно часто в каждом приложении.
Я надеюсь, что теперь вы лучше понимаете, как C # и Ruby сравниваются, когда дело доходит до свойств. Оставьте комментарий, если у вас есть какие-либо вопросы или понимание.