我想使用信息隐藏与属性的set访问器。所以我想触发set访问器而不在其中添加任何值。在set acessor中是值的初始化,如下所示:
/// <summary>
/// load short ref controls
/// </summary>
/// <param name="reset">(re)set cached list</param>
public List<ShortRef_Control> ShortRefControls
{
// (re)set cached list
set
{
tableDictionary["refcontrols"] =
(from src in dataContext.ShortRef_Controls select src).ToList();
}
// return cached list
get
{
return tableDictionary.Get<List<ShortRef_Control>>("refcontrols");
}
}
问题:为什么我要这样做?答案:有两个原因:1。我还想重置这个,我使用带有数据库行的表的缓存列表。更新此表时,我想刷新缓存的列表。我不想为此使用难以读取的setter,因此希望隐藏setter初始化。 2.我使用密钥名称,然后输入错误是可能的,将这些名称保持在一起是一个很好的解决方案。什么有用?可能不是那么好的解决方案:ShortRefControls = null
C#语言缺少什么吗?或者我(错误)以这种方式使用属性。 ShortRefControls = null
工作,但如果我的另一个同事正在维护此代码,他可以得到ShortRefControls值设置为null的印象。
我能想到的最好的解决方案是不要使用属性,并使用单独的GetShortRefControls()和SetShortRefControls()函数。
更新:我(我...我的同事;-)想出了这个:
/// <summary>
/// load short ref controls
/// </summary>
private void SetShortRefControls() => ShortRefControls = (from src in dataContext.ShortRef_Controls select src).ToList();
此私有函数用作setter值。在某种程度上,它的行为类似于设置者的别名或包装器。这有什么不对吗?它是一个不需要的额外功能。
C#版本x?认为ShortRefControls =<
正在初始化没有价值,这是一种在未来的C#版本中处理这种情况的方法,这很奇怪。 = <字符可能偏离任何字符,意味着自我初始化。要让setter的行为像setter一样,=表示ShortRefControls应该得到一个值,<表示它将自己初始化而不作为值传递。
更新2:经过进一步思考后我想出了这个:
/// <summary>
/// load short ref controls
/// </summary>
/// <param name="set">(re)set content cached list</param>
public List<ShortRef_Control> ShortRefControlList(bool set = false)
{
// set or reset cache
if (set)
tableDictionary["refcontrols"] =
(from src in dataContext.ShortRef_Controls select src).ToList();
// always return content of cache
return tableDictionary.Get<List<ShortRef_Control>>("refcontrols");
}
我使用List后缀了methodname,因此我们知道为了清楚起见引用了一个列表。这不是我真正想要的,也不是高效的,因为它总是返回值,所以我将上面的代码看作是一种解决方法。
我尝试过但没有用的东西:ShortRefControls = SelfInitialising。这可以是很好的解决方案。 SelfInitialising是一个具有null值的变量。这清楚地表明了意义。问题是,我不仅在tableDictionary中有ShortRefControls,而且还有更多不同类型的数据,因此不会有这个技巧。
我真正想要的是:一个自我初始化的二传手
那么有没有更好的替代方法来处理这个或者给Visual Studio团队一个很好的建议来扩展语言?
你是对的,语言不支持初始化setter的语法。
在我看来,您展示的代码并不反映您的设计选择。这意味着您正在寻找更好地表达您意图的语法。
在我看来,ShortRefControls
应该是一个只读属性:
public List<ShortRef_Control> ShortRefControls
{
// return cached list
get
{
return tableDictionary.Get<List<ShortRef_Control>>("refcontrols");
}
}
附注:您可以将此缩短为以下内容:
public List<ShortRef_Control> ShortRefControls =>
tableDictionary.Get<List<ShortRef_Control>>("refcontrols");
接下来,更新tableDictionary
的缓存。这不应该在属性设置器中完成:不使用value
参数,因此它不设置属性,它正在做其他事情。调用者会感到困惑,因为此代码不会执行以下操作:
someObject.ShortRefControls = null;
null
被忽略,但ShortRefControls
被设置为其他值。这会让大多数查看代码的开发人员感到惊讶。
方法SetShortRefControls
执行更新字典条目所需的操作。重要的问题是什么时候应该调用它。从代码片段来看,很难说,但这里有一些可能性:
get
的ShortRefControls
访问者的一部分。这将确保字典始终是最新的。但是,它有效地使得不需要保持缓存的值。您可以在构造函数中初始化缓存而不使用setter。然后根据您的预定义条件(ShouldReload)getter可能会重新加载缓存。您可以将Reload方法设置为private / public,具体取决于您是否希望为此类用户提供显式重新加载缓存的能力。
public class TestClass
{
private Dictionary<string, List<ShortRef_Control>> tableDictionary;
public TestClass()
{
ReloadCache();
}
public List<ShortRef_Control> ShortRefControls
{
get
{
if (ShouldReload())
{
ReloadCache();
}
return tableDictionary["refcontrols"];
}
}
public void ReloadCache()
{
tableDictionary["refcontrols"] =
(from src in dataContext.ShortRef_Controls select src).ToList();
}
private bool ShouldReload()
{
return tableDictionary["refcontrols"] == null || !tableDictionary["refcontrols"].Any();
}
}
如何扩展ozanmut的解决方案并使用索引器指定表名?
这将保留ozanmut的清洁模式,它还应解决您处理多个表的问题:
public class Tables
{
private readonly IDictionary<string, List<ShortRef_Control>> tableDictionary = new Dictionary<string, List<ShortRef_Control>>();
public List<ShortRef_Control> this[string tableName]
{
get
{
if (ShouldReload(tableName))
Reload(tableName);
return tableDictionary[tableName];
}
}
public void Reload()
{
foreach (var tableName in tableDictionary.Keys)
Reload(tableName);
}
private void Reload(string tableName)
{
// implement table-specific loading logic here:
switch (tableName)
{
case "refcontrols":
{
tableDictionary[tableName] = (from src in dataContext.ShortRef_Controls select src).ToList();
break;
}
default:
{
throw new NotSupportedException($"invalid table name: {tableName}");
}
}
// note: in the real world I wouldn't use a switch statement to implement the logic, but this is just to
// illustrate the general concept
}
private bool ShouldReload(string tableName)
{
return tableDictionary[tableName] == null || !tableDictionary[tableName].Any();
}
}
public class TestClass
{
private readonly Tables tables = new Tables();
public TestClass()
{
tables.Reload();
}
// this indexer will allow you to access your various tables from outside this class by specifying the table name
public List<ShortRef_Control> this[string name]
{
get { return tables[name]; }
}
// if you need your table names hard-coded, you can create separate properties for each one:
public List<ShortRef_Control> RefControlsTable { get { return this["refcontrols"]; }}
// here's a few examples of how the tables would be accessed or used
public void UseTableRefControl()
{
UseTableRefControl(DoSomethingWithRecord);
}
public void UseTableRefControl(Action<ShortRef_Control> action)
{
TakeActionOnTable("refcontrols", DoSomethingWithRecord);
}
public void TakeActionOnTable(string tableName, Action<ShortRef_Control> action)
{
foreach (var row in tables[tableName])
action(row);
}
private static void DoSomethingWithRecord(ShortRef_Control row)
{
// do something with the row here
}
}