깔끔하고 간결한 코드를 작성하려고 할 때 색인화 된 속성이 부족하다는 사실을 알게되었습니다. 인덱싱 된 속성은 인덱싱 된 클래스 참조를 제공하거나 개별 메서드를 제공하는 것과는 매우 다른 의미를 갖습니다. 인덱싱 된 속성을 구현하는 내부 개체에 대한 액세스를 제공하는 것은 종종 개체 방향의 핵심 구성 요소 중 하나 인 캡슐화를 깨뜨리기 때문에 허용되는 것으로 간주된다는 사실이 약간 혼란 스럽습니다.
이 문제가 자주 발생하지만 오늘 다시 만났으므로 실제 코드 예제를 제공하겠습니다. 작성되는 인터페이스와 클래스는 느슨하게 관련된 정보의 모음 인 애플리케이션 구성을 저장합니다. 명명 된 스크립트 조각을 추가해야했고 명명되지 않은 클래스 인덱서를 사용하면 스크립트 조각이 구성의 일부일 뿐이므로 매우 잘못된 컨텍스트를 암시했을 것입니다.
C #에서 인덱싱 된 속성을 사용할 수 있다면 아래 코드를 구현할 수있었습니다 (구문은 this [key]가 PropertyName [key]로 변경됨).
public interface IConfig
{
string Scripts[string name] { get; set; }
}
internal class Config : IConfig, IXmlConfig
{
#region Application Configuraiton Settings
public string Scripts[string name]
{
get
{
if (!string.IsNullOrWhiteSpace(name))
{
string script;
if (_scripts.TryGetValue(name.Trim().ToLower(), out script))
return script;
}
return string.Empty;
}
set
{
if (!string.IsNullOrWhiteSpace(name))
{
_scripts[name.Trim().ToLower()] = value;
OnAppConfigChanged();
}
}
}
private readonly Dictionary<string, string> _scripts = new Dictionary<string, string>();
#endregion
private void ClearConfig()
{
_scripts.Clear();
}
#region IXmlConfig
void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
{
Debug.Assert(configVersion == 2);
Debug.Assert(appElement != null);
if (_scripts.Count > 0)
{
var scripts = new XElement("Scripts");
foreach (var kvp in _scripts)
{
var scriptElement = new XElement(kvp.Key, kvp.Value);
scripts.Add(scriptElement);
}
appElement.Add(scripts);
}
}
void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
{
Debug.Assert(appElement != null);
ClearConfig();
if (configVersion == 2)
{
var scripts = appElement.Element("Scripts");
if (scripts != null)
foreach (var script in scripts.Elements())
_scripts[script.Name.ToString()] = script.Value;
}
else
throw new ApplicaitonException("Unknown configuration file version " + configVersion);
}
#endregion
}
불행히도 인덱싱 된 속성은 구현되지 않았으므로이를 저장하는 클래스를 구현하고 이에 대한 액세스를 제공했습니다. 이 도메인 모델에서 구성 클래스의 목적은 모든 세부 정보를 캡슐화하는 것이기 때문에 이는 바람직하지 않은 구현입니다. 이 클래스의 클라이언트는 이름으로 특정 스크립트 조각에 액세스하며이를 계산하거나 열거 할 이유가 없습니다.
나는 이것을 다음과 같이 구현할 수 있었다.
public string ScriptGet(string name)
public void ScriptSet(string name, string value)
내가 가져야 할 것 같지만, 이는이 누락 된 기능을 대체하기 위해 인덱스 된 클래스를 사용하는 것이 종종 합리적인 대체물이 아닌 이유를 보여주는 유용한 예시입니다.
인덱싱 된 속성과 유사한 기능을 구현하기 위해 아래 코드를 작성해야했습니다.이 코드는 상당히 길고 복잡하며 읽기, 이해 및 유지 관리가 더 어렵습니다.
public interface IConfig
{
ScriptsCollection Scripts { get; }
}
internal class Config : IConfig, IXmlConfig
{
public Config()
{
_scripts = new ScriptsCollection();
_scripts.ScriptChanged += ScriptChanged;
}
#region Application Configuraiton Settings
public ScriptsCollection Scripts
{ get { return _scripts; } }
private readonly ScriptsCollection _scripts;
private void ScriptChanged(object sender, ScriptChangedEventArgs e)
{
OnAppConfigChanged();
}
#endregion
private void ClearConfig()
{
_scripts.Clear();
}
#region IXmlConfig
void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
{
Debug.Assert(configVersion == 2);
Debug.Assert(appElement != null);
if (_scripts.Count > 0)
{
var scripts = new XElement("Scripts");
foreach (var kvp in _scripts)
{
var scriptElement = new XElement(kvp.Key, kvp.Value);
scripts.Add(scriptElement);
}
appElement.Add(scripts);
}
}
void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
{
Debug.Assert(appElement != null);
ClearConfig();
if (configVersion == 2)
{
var scripts = appElement.Element("Scripts");
if (scripts != null)
foreach (var script in scripts.Elements())
_scripts[script.Name.ToString()] = script.Value;
}
else
throw new ApplicaitonException("Unknown configuration file version " + configVersion);
}
#endregion
}
public class ScriptsCollection : IEnumerable<KeyValuePair<string, string>>
{
private readonly Dictionary<string, string> Scripts = new Dictionary<string, string>();
public string this[string name]
{
get
{
if (!string.IsNullOrWhiteSpace(name))
{
string script;
if (Scripts.TryGetValue(name.Trim().ToLower(), out script))
return script;
}
return string.Empty;
}
set
{
if (!string.IsNullOrWhiteSpace(name))
Scripts[name.Trim().ToLower()] = value;
}
}
public void Clear()
{
Scripts.Clear();
}
public int Count
{
get { return Scripts.Count; }
}
public event EventHandler<ScriptChangedEventArgs> ScriptChanged;
protected void OnScriptChanged(string name)
{
if (ScriptChanged != null)
{
var script = this[name];
ScriptChanged.Invoke(this, new ScriptChangedEventArgs(name, script));
}
}
#region IEnumerable
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return Scripts.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
public class ScriptChangedEventArgs : EventArgs
{
public string Name { get; set; }
public string Script { get; set; }
public ScriptChangedEventArgs(string name, string script)
{
Name = name;
Script = script;
}
}