【问题标题】:Getter property with arguments带参数的 Getter 属性
【发布时间】:2011-05-22 14:46:39
【问题描述】:

我想我以前在什么地方见过它,但现在我不记得也找不到了。有没有办法用参数创建一个 getter 属性?

我的意思是,因为我可以转换“float getSize();”到“浮动大小”:

float getSize() {
    return this.size;
}

float Size {
    get { return this.size; }
}

那么,我可以转换吗,例如,“float getSize(String unit);”到“浮动大小(字符串单位)”或类似的东西?

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

【问题讨论】:

  • 如果 getter 接受参数,它们看起来不是和方法调用完全一样吗?
  • @BoltClock:是的,我想。无论如何,它们都是作为方法调用实现的。 VB.NET确实允许用任意参数定义getter。

标签: c# properties


【解决方案1】:

回答这个问题:不,这是不可能的,正如已经指出的,带有参数的 getter 看起来就像一个方法。

您正在考虑的可能是索引默认属性,如下所示:

class Test
{
    public string this[int index] 
    {
        get { return index.ToString(); } 
    }
}

这允许您对 Test 的实例进行索引,如下所示:

Test t = new Test();
string value = t[1];

【讨论】:

  • 谢谢,我怕不可能。但正如名字所说,我尝试保留属性以获取与对象相关的“属性”或参数,以及操作和计算其他东西的方法。由于没有其他选择,我将不得不使用这些方法:D
  • @Dane411:实际上建议执行除了简单地返回私有字段的值(例如从文件中读取数据)之外执行昂贵操作的getter,或者那些需要接受参数的getter就常规方法而言。将它们命名为GetXXX(),每个人都会很好地理解您的代码。
  • 不错;请注意,从 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

它并不优于常规功能,但有时有这样的可能性很好。

【讨论】:

  • 从 C# 调用此类属性会导致方法调用如下:get_oPair(param)
  • 谢谢@HansVonn,我在 VB 中使用它作为重载属性,并且不得不在 VB 库中的 C# 中使用它。我花了将近一个小时才弄清楚为什么 C# 只能调用默认属性。只要我把“get_”放在它前面,事情就变得活跃起来。谢谢!!
【解决方案3】:

我知道这是一篇旧文章,但我今天想用 C# 来做这件事。无论这是否是一件好事,都可能落在“不”的一边。不过,我在 (https://social.msdn.microsoft.com/forums/en-US/5a25bc83-990e-4657-aa9c-69bca5158d48/overloaded-c-properties-with-arguments?prof=required) 看到了 Mark Jones 发布的一个有趣的想法,但我不太喜欢它的感觉。

所以我根据他写了自己的(我的在.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;
    }
}

稍微介绍一下我的解决方案:我为 getter/setter 选择了 Func 和 Action,因为我不希望这个助手类需要任何关于它所支持的底层属性的知识。相反,拥有该属性的类将具有用于 get_X / set_X(或您希望使用的任何命名约定)的公共(或私有)方法,可以处理所有事情 - 例如验证。

现在我的用例是:我有一个类,它有一个特定类型的内部数组。我有一个默认索引器属性public primaryType this[int index],但它有几个其他类型,它可以理解并且可以转换为/从primaryType。但是,我不能做public otherType this[int index],而且我真的不想做名为“get_OtherType”和“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. */
}

然后在使用此类的其他类/UI 中,“OtherType”比“PrimaryType”更方便他们使用,我可以让他们执行myClass1.OtherType[0] = otherType; 之类的操作,但如果他们使用主要类型,然后他们可以做myClass1[0] = primaryType - 或者如果我只想保持一致/明确,我没有默认索引器属性,我也使用 ParameterizedProperty 作为主要类型,即:myClass1.PrimaryType[0] = primaryType;

再一次,我不确定走这条路是否是个好主意。

【讨论】:

    【解决方案4】:

    通过让属性返回struct,类对象可以合理有效地拥有作为命名索引属性获取器的东西,struct 只是持有对类对象的私有引用并包含一个索引属性获取器,它链接到类中的方法。这样一个单项结构基本上可以免费生成(它可能适合一个寄存器,并且将加载另一个寄存器中的值;JIT 甚至可以识别相同的寄存器可以用于两个目的),所以如果使用这样的 getter 使代码更具可读性,这是一个支持的重要论据。

    不幸的是,结构成员无法指示它们是否修改底层结构,因此无法对索引属性设置器使用相同的方法。即使它会有所帮助,也可以有一个 OrderedCollection&lt;T&gt; 类似的东西:

    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 实现可以安全地用于只读结构。

    【讨论】:

      猜你喜欢
      • 2021-07-19
      • 2017-05-21
      • 2020-02-03
      • 1970-01-01
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 2021-11-11
      相关资源
      最近更新 更多