在C#中命名为索引属性?
一些语言 – 比如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 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 list; string IMyProp.this[int index] { get { return list[index]; } set { list[index] = value; } } // ctor etc. public IMyProp MyProp { get { return this; } } }
优点:
- 没有代理类(占用内存空间,仅用于单一目的和(在最简单的解决方案中)打破封装。
- 简单的解决方案,需要很少的代码来添加另一个索引属性
缺点:
- 每个属性都需要不同的公共接口
- 随着索引属性的增加,该类实现了越来越多的接口
这是将索引属性引入C#的最简单(就代码长度和复杂性而言)的方式。 当然,除非有人发布更短更简单的内容。