【问题标题】:Custom Named Property Indexers自定义命名属性索引器
【发布时间】:2016-12-13 18:32:14
【问题描述】:

是否有任何可能的方法来创建一个使用索引器而不是整个类的全局索引器的属性?这是我想做的事情的要点。请注意,在此示例中,您应该假装我无法移动 data 或创建另一个 Dictionary

class MyClass
{
    protected Dictionary<string,object> data = new Dictionary<string,object>();
    public object Foo[int index]
    {
        get {
            string name = String.Format("foo{0}",index);
            return data[name];
        }
    }
    public object Bar[int index]
    {
        get {
            string name = String.Format("bar{0}",index);
            return data[name];
        }
    }
}

然后我就可以引用名为 foo15bar12data 项目,就像这样

MyClass myClass = new MyClass();
Console.WriteLine(myClass.Foo[15]);
Console.WriteLine(myClass.Bar[12]);

我知道你可以这样做,但这不是我想要的。

    public object this[string index]
    {
        get {
            return data[index];
        }
    }

【问题讨论】:

  • 你为什么首先想要这个?您认为它的行为与普通索引器有何不同?
  • @EJoshuaS,我不想公开data 的所有属性,但我也不想定义FooX,其中X 是一个数字15 次。这导致一遍又一遍地生成相同的代码,并且难以迭代 FooX 属性。我想让它更容易,这样MyClass 的用户就可以引用Foo[15]
  • @EJoshuaS,我怀疑我最终不得不将Foo 设为一个函数(例如public object Foo( int index ) { return data[String.Format("foo{0}",index)]; },但我真的更喜欢使用属性。
  • 似乎特定语法并不比仅使用方法好得多(因为使用普通方法您只需执行 Foo(15),即字符数相同)。

标签: c# c#-4.0 indexing properties


【解决方案1】:

您可以做到,但需要跳过一些障碍。 C# 本身不支持索引属性。我学会了如何使用这篇文章:Easy creation of properties that support indexing in C#

首先声明一个 IndexedProperty 类:

public class IndexedProperty<TIndex, TValue>
{
    Action<TIndex, TValue> setAction;
    Func<TIndex, TValue> getFunc;

    public IndexedProperty(Func<TIndex, TValue> getFunc, Action<TIndex, TValue> setAction)
    {
        this.getFunc = getFunc;
        this.setAction = setAction;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return getFunc(i);
        }
        set
        {
            setAction(i, value);
        }
    }
}

这是一个使用它的例子(参考的帖子包含一个更简单的例子):

    protected Dictionary<DialogResult, string> ButtonNames { get; set; }
    public IndexedProperty<DialogResult, string> ButtonName
    {
        get
        {
            return new IndexedProperty<DialogResult, string>(
                r =>
                {
                    if (ButtonNames.ContainsKey(r)) return ButtonNames[r];
                    return r.ToString();
                }, 
                (r, val) => ButtonNames[r] = val);
        }
    }

稍后...

tableLayoutPanel2.Controls.Add(new Button 
    {
        Text = ButtonName[btn], DialogResult = btn, 
        Anchor = AnchorStyles.Right
    });

ButtonNames 可以从类外部设置,例如:

msgbox.ButtonName[DialogResult.Cancel] = "Don't Do The Thing";

【讨论】:

    【解决方案2】:

    您需要创建一个新类型,让Foo 返回该类型的一个实例,为该其他类型提供一个索引器,并让该索引器执行您希望它执行的任何操作。

    【讨论】:

      【解决方案3】:

      C# 中的类不能有命名索引器,但可以有多个具有不同签名的索引器。 一种可能的方法是使用不同的索引器接口,让您的“命名索引器”只返回转换为正确“接口”的类本身。

      你的例子会变成:

      class MyClass: MyClass.IFooIndexer, MyClass.IBarIndexer
      {
          public interface IFooIndexer { object this[int index] { get; } }
          public interface IBarIndexer { object this[int index] { get; } }
      
          protected Dictionary<string, object> data = new Dictionary<string, object>();
      
          //implementing Interfaces
          object IFooIndexer.this[int index] { 
              get => data[$"foo{index}"]; 
          }
          object IBarIndexer.this[int index] {
              get => data[$"bar{index}"];
          }
      
          // these are your "named" indexers
          public IFooIndexer Foo => (IFooIndexer)this;
          public IBarIndexer Bar => (IBarIndexer)this;
          
          //add some data to test with
          public MyClass()
          {
              data.Add("foo15", "<foo 15 value>");
              data.Add("bar12", "<bar 12 value>");
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-06
        • 2011-04-27
        • 1970-01-01
        • 2014-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-30
        相关资源
        最近更新 更多