轻松创建支持C#索引的属性

在C#中,我发现索引属性非常有用。 例如:

var myObj = new MyClass(); myObj[42] = "hello"; Console.WriteLine(myObj[42]); 

但据我所知,没有语法糖来支持自己支持索引的字段(如果我错了请纠正我)。 例如:

 var myObj = new MyClass(); myObj.field[42] = "hello"; Console.WriteLine(myObj.field[42]); 

我需要这个的原因是我已经在我的类上使用索引属性,但我有GetNumX()GetX()SetX()函数如下:

 public int NumTargetSlots { get { return _Maker.NumRefs; } } public ReferenceTarget GetTarget(int n) { return ReferenceTarget.Create(_Maker.GetReference(n)); } public void SetTarget(int n, ReferenceTarget rt) { _Maker.ReplaceReference(n, rt._Target, true); } 

你可能会看到将这些暴露为一个可索引的字段属性会更有意义。 我可以编写一个自定义类来实现这一点,每次我想要语法糖,但所有的样板代码似乎都是不必要的。

所以我编写了一个自定义类来封装样板,并且可以轻松创建可以编制索引的属性。 这样我可以添加一个新属性,如下所示:

 public IndexedProperty TargetArray { get { return new IndexedProperty( (int n) => GetTarget(n), (int n, ReferenceTarget rt) => SetTarget(n, rt)); } } 

这个新的IndexedProperty类的代码如下所示:

 public class IndexedProperty { Action setAction; Func getFunc; public IndexedProperty(Func getFunc, Action setAction) { this.getFunc = getFunc; this.setAction = setAction; } public ValueT this[IndexT i] { get { return getFunc(i); } set { setAction(i, value); } } } 

所以我的问题是: 有没有更好的方法来做所有这些

具体来说,在C#中是否有更惯用的方法来创建可索引的字段属性,如果不是,我怎么能改进我的IndexedProperty类?

编辑:经过进一步的研究,Jon Skeet称之为“ 命名索引器 ”。

嗯,最简单的是让属性返回一个实现IList的对象。

请记住,仅仅因为它实现IList并不意味着它本身就是一个集合,只是它实现了某些方法。

这在技术上不是使用StackOverflow的正确方法,但我发现你的想法非常有用,我扩展了它。 如果我发布了我想出的内容以及如何使用它的示例,我认为这会节省人们的时间。

首先,我需要能够支持get-only和set-only属性,因此我对这些场景的代码略有不同:

获取和设置 (非常小的更改):

 public class IndexedProperty { readonly Action SetAction; readonly Func GetFunc; public IndexedProperty(Func getFunc, Action setAction) { this.GetFunc = getFunc; this.SetAction = setAction; } public TValue this[TIndex i] { get { return GetFunc(i); } set { SetAction(i, value); } } } 

仅限获取:

 public class ReadOnlyIndexedProperty { readonly Func GetFunc; public ReadOnlyIndexedProperty(Func getFunc) { this.GetFunc = getFunc; } public TValue this[TIndex i] { get { return GetFunc(i); } } } 

仅限设置:

 public class WriteOnlyIndexedProperty { readonly Action SetAction; public WriteOnlyIndexedProperty(Action setAction) { this.SetAction = setAction; } public TValue this[TIndex i] { set { SetAction(i, value); } } } 

这是一个简单的用法示例。 我inheritance了Collection并创建了一个命名索引器,就像Jon Skeet所说的那样。 这个例子很简单,不实用:

 public class ExampleCollection : Collection { public IndexedProperty ExampleProperty { get { return new IndexedProperty(GetIndex, SetIndex); } } private T GetIndex(int index) { return this[index]; } private void SetIndex(int index, T value) { this[index] = value; } } 

Wild中的ExampleCollection

这个匆忙构造的unit testing显示了在项目中的ExampleCollection时它的外观:

 [TestClass] public class IndexPropertyTests { [TestMethod] public void IndexPropertyTest() { var MyExample = new ExampleCollection(); MyExample.Add("a"); MyExample.Add("b"); Assert.IsTrue(MyExample.ExampleProperty[0] == "a"); Assert.IsTrue(MyExample.ExampleProperty[1] == "b"); MyExample.ExampleProperty[0] = "c"; Assert.IsTrue(MyExample.ExampleProperty[0] == "c"); } } 

最后,如果您想使用仅限get和only-only版本,则如下所示:

  public ReadOnlyIndexedProperty ExampleProperty { get { return new ReadOnlyIndexedProperty(GetIndex); } } 

要么:

  public WriteOnlyIndexedProperty ExampleProperty { get { return new WriteOnlyIndexedProperty(SetIndex); } } 

在这两种情况下,结果的工作方式与您期望只使用get / only-only属性的方式相同。

我认为您发布的设计是要走的路,我将定义一个界面的一个区别:

 public interface IIndexed { ValueT this[IndexT i] { get; set; } } 

对于常见情况,我会使用您在原始问题中放置的类(将实现此接口)。

如果基类库为我们提供了合适的接口会很好,但事实并非如此。 在这里返回IList将是一种变态。

这并没有回答你的问题,但有趣的是,CIL支持制作你所描述的属性 – 某些语言(例如,F#)也允许你以这种方式定义它们。

C#中的this[]索引器只是其中一个的特定实例,在您构建应用程序时将其重命名为Item 。 C#编译器只知道如何读取这个,所以如果你在F#库中编写一个名为Target的“命名索引器”,并尝试在C#中使用它,你可以访问该属性的唯一方法是通过... get_Target(int)void set_Target(int, ...)方法。 吮吸。

为什么不让你的类inheritanceIList然后你可以使用索引并添加自己的属性。 虽然您仍然可以使用添加和删除function,但不要使用它们。 另外,您可能会发现让它们在路上更加有用。

有关列表和数组的更多信息,请查看: 使用数组或列表<>哪个更好?

编辑:

MSDN上有一篇关于您可能想要查看的索引属性的文章。 似乎并不复杂,只是单调乏味。

http://msdn.microsoft.com/en-us/library/aa288464(VS.71).aspx

还有另一个选项可以创建替代的Add方法,但根据对象的类型,可能无法始终调用add方法。 这里解释一下:

如何在C#中覆盖List 的Add方法?

编辑2:与第一篇文章类似

为什么你的类中没有隐藏的列表对象,然后只是创建自己的方法来获取数据。 这样就看不到添加和删除,列表已经编入索引。

另外你的意思是“命名索引器”是你在寻找行[“My_Column_Name”]的等价物。 我发现这篇MSDN文章可能很有用,因为它似乎显示了实现该属性的基本方法。

http://msdn.microsoft.com/en-us/library/146h6tk5.aspx

 class Test { private List index; public T this[string name]{ get; set; } public T this[int i] { get { return index[i]; } set { index[i] = value; } } } 

经过一些研究,我提出了一个稍微不同的解决方案,更符合我的需求。 这个例子有点炮制,但它确实适合我需要它来适应它。

用法:

 MyClass MC = new MyClass(); int x = MC.IntProperty[5]; 

以及使其工作的代码:

 public class MyClass { public readonly IntIndexing IntProperty; public MyClass() { IntProperty = new IntIndexing(this); } private int GetInt(int index) { switch (index) { case 1: return 56; case 2: return 47; case 3: return 88; case 4: return 12; case 5: return 32; default: return -1; } } public class IntIndexing { private MyClass MC; internal IntIndexing(MyClass mc) { MC = mc; } public int this[int index] { get { return MC.GetInt(index); } } } } 

尝试显式实现的接口,如回复中提出的第二种方式所示: C#中的命名索引属性?