#c# #properties
#c# #свойства
Вопрос:
Я предполагаю, что я видел это где-то раньше, но теперь я не могу ни вспомнить, ни найти это. Есть ли способ создать свойство getter с аргументами?
Я имею в виду, поскольку я могу преобразовать «float getSize();» в «float Size»:
float getSize() {
return this.size;
}
float Size {
get { return this.size; }
}
Тогда, могу ли я преобразовать, например, «float getSize(строковая единица);» в «float Size(строковая единица)» или что-то в этом роде?
float getSize(String unit) {
return this.size;
}
float Size(String unit) {
get {
if (unit == Unit.Meters)
return this.size/100;
else
return this.size;
}
}
Я думаю, что на самом деле нет никакой проблемы с использованием функции вообще, но может выглядеть лучше так: P
Комментарии:
1. Если бы получатели принимали аргументы, разве они не выглядели бы точно так же, как вызовы методов?
2. @BoltClock: Да, я полагаю. В любом случае они реализованы как вызовы методов под капотом. И VB.NET позволяет определять геттеры с произвольными параметрами.
Ответ №1:
Чтобы ответить на вопрос: Нет, это невозможно, и, как уже указывалось, средство получения с параметром будет выглядеть точно так же, как метод.
То, о чем вы думаете, может быть индексированным свойством по умолчанию, которое выглядит следующим образом:
class Test
{
public string this[int index]
{
get { return index.ToString(); }
}
}
Это позволяет вам индексировать экземпляр Test, например:
Test t = new Test();
string value = t[1];
Комментарии:
1. Спасибо, я боялся, что это будет невозможно. Но, как следует из названия, я стараюсь сохранять свойства для получения «свойств» или параметров, связанных с объектом, и методы для манипулирования и вычисления других вещей. Поскольку другого выбора нет, тогда мне придется использовать методы: D
2. @Dane411: На самом деле рекомендуется, чтобы средства получения, которые выполняют дорогостоящие операции, помимо простого возврата значения частного поля (например, чтение данных из файла), или те, которым необходимо принимать параметры, были реализованы в терминах обычного метода. Назовите их
GetXXX()
, и все прекрасно поймут ваш код.3. Приятно; обратите внимание, что с точки зрения CLR это превращается в параметризованное свойство с именем
Item
, которое в качестве альтернативы может быть вызвано с помощью синтаксиса метода (t.Item(1)
вместоt[1]
).
Ответ №2:
Интересно, что наличие свойства с параметром возможно в VB.NET , вот так:
Public ReadOnly Property oPair(param As String) As Result
Get
'some code depends on param
End Get
End Property
Это не превосходит обычную функцию, но иногда приятно иметь такую возможность.
Комментарии:
1. Вызов такого свойства из C # приводит к вызову метода, подобного: get_oPair(параметр)
2. Спасибо @HansVonn, я использую это в VB как перегруженные свойства, и мне пришлось использовать это в C # из библиотеки VB. Мне потребовался почти час, чтобы выяснить, почему C # может вызывать только свойство по умолчанию. Как только я поставил перед ним «get_», все ожило. Спасибо!!
Ответ №3:
Я знаю, что это старый пост, но я столкнулся с желанием сделать это сегодня на C #. Хорошо это или нет, вероятно, зависит от того, на стороне «не». Однако я наткнулся на интересную идею, опубликованную Марком Джонсом на ( https://social.msdn.microsoft.com/forums/en-US/5a25bc83-990e-4657-aa9c-69bca5158d48/overloaded-c-properties-with-arguments?prof=required ), но мне не совсем понравилось это ощущение.
Итак, я написал свое собственное на основе его (мое в .Net 5.0 с Nullable = enable):
class ParameterizedProperty<T>
{
private readonly Func<int, T> getter;
private readonly Action<int, T> setter;
public T this[int index]
{
get => this.getter(index);
set => this.setter(index, value);
}
public ParameterizedProperty(Func<int, T> getter, Action<int, T> setter)
{
this.getter = getter;
this.setter = setter;
}
}
class NamedParameterizedProperty<T>
{
private readonly Func<int, T> indexedGetter;
private readonly Action<int, T> indexedSetter;
private readonly Func<string, T> namedGetter;
private readonly Action<string, T> namedSetter;
public T this[int index]
{
get => this.indexedGetter(index);
set => this.indexedSetter(index, value);
}
public T this[string name]
{
get => this.namedGetter(name);
set => this.namedSetter(name, value);
}
public NamedParameterizedProperty(Func<int, T> indexedGetter, Action<int, T> indexedSetter, Func<string, T> namedGetter, Action<string, T> namedSetter)
{
this.indexedGetter = indexedGetter;
this.indexedSetter = indexedSetter;
this.namedGetter = namedGetter;
this.namedSetter = namedSetter;
}
}
class ReadOnlyParameterizedProperty<T>
{
private readonly Func<int, T> getter;
public T this[int index] => this.getter(index);
public ReadOnlyParameterizedProperty(Func<int, T> getter)
{
this.getter = getter;
}
}
class ReadOnlyNamedParameterizedProperty<T>
{
private readonly Func<int, T> indexedGetter;
private readonly Func<string, T> namedGetter;
public T this[int index] => this.indexedGetter(index);
public T this[string name] => this.namedGetter(name);
public ReadOnlyNamedParameterizedProperty(Func<int, T> indexedGetter, Func<string, T> namedGetter)
{
this.indexedGetter = indexedGetter;
this.namedGetter = namedGetter;
}
}
Итак, немного о моем решении: я выбрал Func<> amp; Action<> для getters / setters, потому что я не хотел, чтобы этому вспомогательному классу требовались какие-либо знания о базовом свойстве, которое он будет поддерживать. Вместо этого класс, которому принадлежит свойство, будет иметь общедоступные (или частные) методы для get_X / set_X (или любое другое соглашение об именовании, которое вы хотите использовать), которые будут обрабатывать все — например, проверку.
Теперь что касается моего варианта использования для этого: у меня был класс, который имеет внутренний массив определенного типа. У меня есть свойство индексатора по умолчанию public primaryType this[int index]
, но у него есть пара других типов, которые он понимает и может конвертировать в / из для primaryType. Однако я не могу этого сделать public otherType this[int index]
, и я действительно не хотел делать общедоступные методы, называемые чем-то вроде ‘get_OtherType` amp; ‘set_OtherType’.
Эти вспомогательные классы позволяют мне делать что-то вроде:
public ParameterizedProperty<OtherType> OtherType { get; }
public MyClass()
{
this.OtherType = new(get_OtherType, set_OtherType);
}
private OtherType get_OtherType(int index)
{
/* validate index, convert PrimaryType at index to OtherType and return. */
}
private void set_OtherType(int index, OtherType value)
{
/* validate index, validate value, convert to PrimaryType and set to internal array. */
}
Затем в других классах / пользовательских интерфейсах, которые используют этот класс, где с ‘OtherType’ им удобнее работать, чем с ‘PrimaryType’, я могу заставить их делать что-то вроде myClass1.OtherType[0] = otherType;
, но если они работают с основным типом, то они могут делать myClass1[0] = primaryType
— или, если я просто хочу быть последовательным / явным, у меня нет свойства индексатора по умолчанию, и я использую ParameterizedProperty и для основного типа, т.Е.: myClass1.PrimaryType[0] = primaryType;
Опять же, я не уверен, является ли это хорошей идеей для прохождения этого маршрута или нет.
Ответ №4:
Для объекта класса возможно разумно эффективно иметь что-то, что ведет себя как средство получения именованного индексированного свойства, имея свойство return a struct
, которое просто содержит закрытую ссылку на объект класса и включает средство получения индексированного свойства, которое привязывается к методу в классе. Такая структура из одного элемента может быть сгенерирована практически бесплатно (она, вероятно, может поместиться в регистр и будет загружена со значением, находящимся в другом регистре; JIT может даже распознать, что один и тот же регистр может использоваться для обеих целей), поэтому, если использование такого средства получения делает код более читаемым, это существенный аргумент в пользу.
К сожалению, неспособность членов struct указывать, изменяют ли они базовую структуру или нет, делает невозможным использование того же подхода для средства установки индексированных свойств. Хотя это было бы полезно, можно было бы иметь OrderedCollection<T>
что-то вроде:
struct ByIndexAccessor {
OrderedCollection<T> owner;
ByIndexAccessor(OrderedCollection<T> o) { owner = o; }
T this[int index] {
get { return owner.handleGet(index); }
set { owner.handleSet(index, value); }
}
}
ByIndexAccessor ByIndex {
get {return new ByIndexAccessor(this); }
}
и скажем, myCollection.ByIndex[3] = newThing;
C # отклонил бы такой код, потому что у него нет способа узнать, что эта конкретная индексированная set
реализация может безопасно использоваться в структуре, доступной только для чтения.