patterncsharpMinor
Indexed Properties in C#
Viewed 0 times
propertiesindexedstackoverflow
Problem
I was challenged to implement a mechanmism to support indexed properties in a class structure.
I wanted to avoid the need to implement an abstract property indexer class every time you want to implement a new T just because the backing store has changed. Semantically, if C# did provide indexed properties, the get/set logic would be on the class declaring the indexed property (as there would be no type).
So I implemented the following logic:
An example implementation:
What he doesn't like about this is twofold:
Can you see ways in which this can be improved?
I wanted to avoid the need to implement an abstract property indexer class every time you want to implement a new T just because the backing store has changed. Semantically, if C# did provide indexed properties, the get/set logic would be on the class declaring the indexed property (as there would be no type).
So I implemented the following logic:
public interface IIndexedProperty
{
TValue this[TKey index] { get; set; }
}
public class IndexedProperty : IIndexedProperty
{
private Func Getter { get; set; }
private Action Setter { get; set; }
public IndexedProperty(Func getter, Action setter)
{
this.Getter = getter;
this.Setter = setter;
}
public TValue this[TKey index]
{
get
{
return Getter(index);
}
set
{
Setter(index, value);
}
}
}An example implementation:
public class MyClass
{
public IIndexedProperty Values { get; private set; }
private Dictionary ValueDictionary { get; set; }
public MyClass()
{
Values = new IndexedProperty(index => ValueDictionary[index], (index, value) => ValueDictionary[index] = value);
ValueDictionary = new Dictionary();
}
}What he doesn't like about this is twofold:
- It isn't reusable (you have to implement the logic in your defining class each time you use it)
- It holds references to two delegates.
Can you see ways in which this can be improved?
Solution
This implementation allows for reusability of the indexer backing logic, as shown on MyClass two having two indexers using the same backing implementation. This pattern is much more reusable.
public abstract class PropertyIndexer
{
public abstract TValue this[TKey index] { get; set; }
}
public class MyClass
{
private class StringDictionaryPropertyIndexer : PropertyIndexer
{
private Dictionary BackingCollection { get; set; }
public StringDictionaryPropertyIndexer(Dictionary backing)
{
this.BackingCollection = backing;
}
public override string this[string index]
{
get
{
string value;
if (BackingCollection.TryGetValue(index, out value))
return value;
return null;
}
set
{
BackingCollection[index] = value;
}
}
}
private Dictionary data { get; set; }
public PropertyIndexer Data { get; private set; }
private Dictionary alternateData { get; set; }
public PropertyIndexer AlternateData { get; private set; }
public MyClass()
{
data = new Dictionary();
Data = new StringDictionaryPropertyIndexer(data);
alternateData = new Dictionary();
AlternateData = new StringDictionaryPropertyIndexer(alternateData);
}
}Code Snippets
public abstract class PropertyIndexer<TKey, TValue>
{
public abstract TValue this[TKey index] { get; set; }
}
public class MyClass
{
private class StringDictionaryPropertyIndexer : PropertyIndexer<string, string>
{
private Dictionary<string, string> BackingCollection { get; set; }
public StringDictionaryPropertyIndexer(Dictionary<string, string> backing)
{
this.BackingCollection = backing;
}
public override string this[string index]
{
get
{
string value;
if (BackingCollection.TryGetValue(index, out value))
return value;
return null;
}
set
{
BackingCollection[index] = value;
}
}
}
private Dictionary<string, string> data { get; set; }
public PropertyIndexer<string, string> Data { get; private set; }
private Dictionary<string, string> alternateData { get; set; }
public PropertyIndexer<string, string> AlternateData { get; private set; }
public MyClass()
{
data = new Dictionary<string, string>();
Data = new StringDictionaryPropertyIndexer(data);
alternateData = new Dictionary<string, string>();
AlternateData = new StringDictionaryPropertyIndexer(alternateData);
}
}Context
StackExchange Code Review Q#82792, answer score: 2
Revisions (0)
No revisions yet.