C#中的命名索引属性
本文关键字:索引 属性 | 更新日期: 2023-09-27 17:58:35
一些语言(如Delphi)有一种非常方便的创建索引器的方法:不仅可以对整个类进行索引,甚至可以对单个属性进行索引,例如:
type TMyClass = class(TObject)
protected
function GetMyProp(index : integer) : string;
procedure SetMyProp(index : integer; value : string);
public
property MyProp[index : integer] : string read GetMyProp write SetMyProp;
end;
这可以很容易地使用:
var c : TMyClass;
begin
c = TMyClass.Create;
c.MyProp[5] := 'Ala ma kota';
c.Free;
end;
有没有一种方法可以在C#中轻松实现同样的效果?
众所周知的解决方案是创建一个代理类:
public class MyClass
{
public class MyPropProxy
{
private MyClass c;
// ctor etc.
public string this[int index]
{
get
{
return c.list[index];
}
set
{
c.list[index] = value;
}
}
}
private List<string> list;
private MyPropProxy myPropProxy;
// ctor etc.
public MyPropProxy MyProp
{
get
{
return myPropProxy;
}
}
}
但是(例外的是,这实际上解决了问题),这个解决方案主要只引入了缺点:
- 它会导致代码被(可能)许多小型代理类污染
- 所提出的解决方案稍微破坏了封装(内部类访问外部类的私有成员),更好的解决方案是将列表的实例传递给
MyPropProxy
的ctor,这将需要更多的代码 - 我不建议公开内部助手类。可以通过引入额外的接口来解决这个问题,但这甚至是一个需要实现的实体(测试、维护等)
不过,还有另一种方法。它对代码的污染也比前一个有所减少:
public interface IMyProp
{
string this[int index] { get; }
}
public class MyClass : IMyProp
{
private List<string> list;
string IMyProp.this[int index]
{
get
{
return list[index];
}
set
{
list[index] = value;
}
}
// ctor etc.
public IMyProp MyProp
{
get
{
return this;
}
}
}
优点:
- 没有代理类(它们占用内存中的空间,仅用于单一目的,并且(在最简单的解决方案中)破坏封装
- 简单的解决方案,只需少量代码即可添加另一个索引属性
缺点:
- 每个属性都需要不同的公共接口
- 随着索引属性的增加,类实现了越来越多的接口
这是向C#引入索引属性的最简单的方法(就代码长度和复杂性而言)。当然,除非有人发布更短、更简单的帖子。
这是基于H B的注释,但扩展了一点,并作为一个代码块(使复制更容易):
public interface IIndexedProperty<TValue> : IIndexedProperty<int, TValue> {}
public interface IReadOnlyIndexedProperty<out TValue> : IReadOnlyIndexedProperty<int, TValue> {}
public interface IIndexedProperty<in TIndex, TValue>
{
TValue this[TIndex index] { get; set; }
}
public interface IReadOnlyIndexedProperty<in TIndex, out TValue>
{
TValue this[TIndex index] { get; }
}
这使用了C#9.0中的共变量,所以如果您使用的是旧版本,请去掉in
和out
语句。
优点:大多数常见的索引属性使用简单的int
索引,因此,如果索引只需要是int
,则可以使用更简单的类/属性签名,但也可以使用非int
索引。
还根据您的需要提供读/写实现和只读实现。
我在尝试使用后发现:int
仅索引类签名的快捷方式。显然,它仍然需要该房产的完整签名。
IE:
public MyClass : IIndexedProperty<string>
{
public IIndexedProperty<string> MyString => this;
string IIndexedPropety<int, string>.this[int index] { get; set; }
}