c# - 在 C# 中轻松创建支持索引的属性

标签 c# properties indexed-properties

在 C# 中,我找到了 indexed properties非常有用。例如:

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<ReferenceTarget> TargetArray  {
    get { 
       return new IndexedProperty<int, ReferenceTarget>(
           (int n) => GetTarget(n), 
           (int n, ReferenceTarget rt) => SetTarget(n, rt));
       }
}

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

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 类?

编辑:经过进一步研究,Jon Skeet 称其为“named indexer”。

最佳答案

2022 年编辑: 这继续获得选票,但我今天可能不会使用它,主要是因为它确实以一种不理想的方式插入垃圾收集,如果该属性(property)受到了很多打击。我记得这是一个复杂的话题,我现在不想深入研究它,但我想知道索引器今天是否可以解决这个问题。请参阅:https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/


我发现你的想法很有用,所以我扩展了它。这在技术上可能不是一个正确的答案,因为我不确定它是否直接回答了你的问题,但我认为它可能对来这里寻找属性(property)索引器的人有用。

首先,我需要能够支持 get-only 和 set-only 属性,所以我针对这些场景对您的代码做了一些改动:

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

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 所说的那样。这个例子是为了简单,而不是实用:

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 时它的样子:

[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 版本,看起来像这样:

    public ReadOnlyIndexedProperty<int, T> ExampleProperty
    {
        get
        {
            return new ReadOnlyIndexedProperty<int, T>(GetIndex);
        }
    }

或者:

    public WriteOnlyIndexedProperty<int, T> ExampleProperty
    {
        get
        {
            return new WriteOnlyIndexedProperty<int, T>(SetIndex);
        }
    }

在这两种情况下,结果的工作方式与您期望的仅获取/仅设置属性的行为方式相同。

关于c# - 在 C# 中轻松创建支持索引的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3344620/

相关文章:

c# - 如何允许用户在 c# 中的 datagridview 组合框中手动输入

c# "There is not enough space on the disk"实际上有

c# - StreamReader.ReadLine 和 CR

c++ - 没有可行的重载

java - ant 任务将属性文件复制到 java 构建目录中的相应位置

java - 在struts的索引属性中添加新项目

java - 如何使用PropertyUtils获取arraylist的属性

c# - 当满足计数或长度条件时使用 TakeWhile 打破循环

c# - 如何在 C# 中迭代​​匿名对象的属性?

c# - 识别 LINQ 表达式中的索引器