c#命名属性包装上的索引器

本文关键字:索引 包装 属性 | 更新日期: 2023-09-27 17:52:38

我有遗留代码,我试图淘汰,我不能改变。它是一个基于列的数据存储类,可以根据列的名称对其进行索引。像这样:

StorageClassName scn = new StorageClassName("Property_Name",new double[]{1,2,3});
double[] d = scn["Property_Name"];

我正在尝试创建某种包装类,它将允许我实现接口/类继承。

BetterStorageClassName bscn = scn;
double[] d = bscn.PropertyName;

完成这个包装器类的最好方法是什么?它不一定是隐式的。

编辑:我省略了这个问题的主要部分。第一部分的答案还是不错的。我怎么用另一种方式进行转换呢?
 double[] d = bscn["PropertyName"];

我假设这在某种程度上使用了反射

c#命名属性包装上的索引器

如果你有一个带有字符串indexer的类并且你想把它转换成属性,你可以使用dynamic:

class StorageClassName : Dictionary<string, double[]>
{}
class DynamicStorageClassName : DynamicObject
{
    private readonly StorageClassName m_storageClassName;
    public DynamicStorageClassName(StorageClassName storageClassName)
    {
        m_storageClassName = storageClassName;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (m_storageClassName.ContainsKey(binder.Name))
        {
            result = m_storageClassName[binder.Name];
            return true;
        }
        return base.TryGetMember(binder, out result);
    }
}

用法:

var scn = new StorageClassName { { "PropertyName", new double[] { 1, 2, 3 } } };
dynamic dscn = new DynamicStorageClassName(scn);
Console.WriteLine(dscn.PropertyName[1]);

请记住,使用dynamic会付出一些性能损失,如果属性集不会改变,您可能应该像其他人建议的那样使用普通属性。

另一方面,如果您有一个具有普通属性的类,并希望将其转换为使用indexer,则可以使用反射:

class StaticStorageClassName
{
    public double[] PropertyName { get; set; }
    public StaticStorageClassName()
    {
        PropertyName = new double[] { 1, 2, 3 };
    }
}
class ReverseStorageClassName
{
    private readonly StaticStorageClassName m_staticStorageClassName;
    public ReverseStorageClassName(StaticStorageClassName staticStorageClassName)
    {
        m_staticStorageClassName = staticStorageClassName;
    }
    public double[] this[string name]
    {
        get
        {
            var propertyInfo = typeof(StaticStorageClassName).GetProperty(name);
            if (propertyInfo == null)
                throw new ArgumentException();
            return (double[])propertyInfo.GetValue(m_staticStorageClassName, null);
        }
    }
}

用法:

var sscn = new StaticStorageClassName();
var rscn = new ReverseStorageClassName(sscn);
Console.WriteLine(rscn["PropertyName"][2]);

如果不同的属性具有不同的类型,则必须将索引器的返回类型设置为objectdynamic

注意,如果使用DynamicObject实现StaticStorageClassName,则使用反射将不起作用。

对于给出的示例,我想象它是这样实现的:

public double[] PropertyName
{
    get { return wrapped["Property_Name"]; }
    set { wrapped["Property_Name"] = value; }
}

您是否试图避免编写所有这些包装?或者进行类型转换?

从你的问题,我猜你想有完整的命名属性,而不是基于字符串的键。在这种情况下,您只需为访问提供属性:

class BetterStorageClassName : StorageClassName 
{
    public double[] PropertyName
    {
        get { return this["Property_Name"]; }
        set { this["Property_Name"] = value; }
    }
}

但是这个方法有几个注意事项:

  • 这意味着属性是固定的,不会真正改变,否则你最终会不断修改包装器。
  • 属性不能在运行时动态定义。
  • 你必须有一个包装类的每一组属性。