在 C# 中轻松创建支持索引的属性 [英] Easy creation of properties that support indexing in C#
问题描述
在 C# 中,我发现 索引属性有用.例如:
In C# I find indexed properties extremely useful. For example:
var myObj = new MyClass();
myObj[42] = "hello";
Console.WriteLine(myObj[42]);
但是据我所知,没有语法糖来支持本身支持索引的字段(如果我错了,请纠正我).例如:
However as far as I know there is no syntactic sugar to support fields that themselves support indexing (please correct me if I am wrong). For example:
var myObj = new MyClass();
myObj.field[42] = "hello";
Console.WriteLine(myObj.field[42]);
我需要这个的原因是我已经在我的班级上使用了 index 属性,但是我有 GetNumX()
、GetX()
和 SetX()
函数如下:
The reason I need this is that I am already using the index property on my class, but I have GetNumX()
, GetX()
, and SetX()
functions as follows:
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);
}
正如您可能看到的,将这些作为一个可索引的字段属性公开会更有意义.每次我想要语法糖时,我都可以编写一个自定义类来实现这一点,但所有样板代码似乎都没有必要.
As you can probably see exposing these as one indexable field property would make more sense. I could write a custom class to achieve this every time I want the syntactic sugar but all of the boilerplate code just seem unnecessary.
所以我编写了一个自定义类来封装样板文件,并使创建可索引的属性变得容易.通过这种方式,我可以添加一个新属性,如下所示:
So I wrote a custom class to encapsulate the boilerplate and to make it easy to create properties that can be indexed . This way I can add a new property as follows:
public IndexedProperty<ReferenceTarget> TargetArray {
get {
return new IndexedProperty<int, ReferenceTarget>(
(int n) => GetTarget(n),
(int n, ReferenceTarget rt) => SetTarget(n, rt));
}
}
这个新的 IndexedProperty 类的代码如下所示:
The code for this new IndexedProperty class looks like:
public class IndexedProperty<IndexT, ValueT>
{
Action<IndexT, ValueT> setAction;
Func<IndexT, ValueT> getFunc;
public IndexedProperty(Func<IndexT, ValueT> getFunc, Action<IndexT, ValueT> setAction)
{
this.getFunc = getFunc;
this.setAction = setAction;
}
public ValueT this[IndexT i]
{
get {
return getFunc(i);
}
set {
setAction(i, value);
}
}
}
所以我的问题是:是否有更好的方法来完成所有这些?
具体来说,在 C# 中是否有更惯用的方法来创建可索引字段属性,如果没有,我该如何改进我的 IndexedProperty
类?
Well to be specific, is there a more idiomatic way in C# to create an indexable field property, and if not how could I improve my IndexedProperty
class?
经过进一步研究,Jon Skeet 将其称为命名索引器".
After further research, Jon Skeet calls this a "named indexer".
推荐答案
我发现你的想法很有用,所以我扩展了它.这在技术上可能不是一个正确的答案,因为我不确定它是否完全回答了您的问题,但我认为它可能对来这里寻找财产索引器的人有用.
I found your idea useful, so I extended it. This may not technically be a proper answer since I'm not sure it squarely answers your question, but I thought it might be useful to people who came here looking for property indexers.
首先,我需要能够支持 get-only 和 set-only 属性,因此我针对这些场景对您的代码进行了一些细微的修改:
First, I needed to be able to support get-only and set-only properties, so I made a slight variation of your code for these scenarios:
获取和设置(非常小的改动):
public class IndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
readonly 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);
}
}
}
仅获取:
public class ReadOnlyIndexedProperty<TIndex, TValue>
{
readonly Func<TIndex, TValue> GetFunc;
public ReadOnlyIndexedProperty(Func<TIndex, TValue> getFunc)
{
this.GetFunc = getFunc;
}
public TValue this[TIndex i]
{
get
{
return GetFunc(i);
}
}
}
仅设置:
public class WriteOnlyIndexedProperty<TIndex, TValue>
{
readonly Action<TIndex, TValue> SetAction;
public WriteOnlyIndexedProperty(Action<TIndex, TValue> setAction)
{
this.SetAction = setAction;
}
public TValue this[TIndex i]
{
set
{
SetAction(i, value);
}
}
}
示例
这是一个简单的使用示例.我从 Collection 继承并创建了一个命名索引器,正如 Jon Skeet 所说的那样.这个例子是为了简单而不实用:
Here's a simple usage example. I inherit from Collection and create a named indexer, as Jon Skeet called it. This example is intended to be simple, not practical:
public class ExampleCollection<T> : Collection<T>
{
public IndexedProperty<int, T> ExampleProperty
{
get
{
return new IndexedProperty<int, T>(GetIndex, SetIndex);
}
}
private T GetIndex(int index)
{
return this[index];
}
private void SetIndex(int index, T value)
{
this[index] = value;
}
}
ExampleCollection in the Wild
这个匆忙构建的单元测试显示了在项目中使用 ExampleCollection 时的外观:
This hastily constructed unit test shows how it looks when you ExampleCollection in a project:
[TestClass]
public class IndexPropertyTests
{
[TestMethod]
public void IndexPropertyTest()
{
var MyExample = new ExampleCollection<string>();
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 和 set-only 版本,看起来像这样:
Finally, if you want to use the get-only and set-only versions, that looks like this:
public ReadOnlyIndexedProperty<int, T> ExampleProperty
{
get
{
return new ReadOnlyIndexedProperty<int, T>(GetIndex);
}
}
或者:
public WriteOnlyIndexedProperty<int, T> ExampleProperty
{
get
{
return new WriteOnlyIndexedProperty<int, T>(SetIndex);
}
}
在这两种情况下,结果的工作方式与您期望的 get-only/set-only 属性的行为方式相同.
In both cases, the result works the way you would expect a get-only/set-only property to behave.
这篇关于在 C# 中轻松创建支持索引的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!