如何在c#中实现自初始化setter

问题描述 投票:-1回答:3

我想使用信息隐藏与属性的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团队一个很好的建议来扩展语言?

c# properties set accessor
3个回答
3
投票

你是对的,语言不支持初始化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执行更新字典条目所需的操作。重要的问题是什么时候应该调用它。从代码片段来看,很难说,但这里有一些可能性:

  1. 作为getShortRefControls访问者的一部分。这将确保字典始终是最新的。但是,它有效地使得不需要保持缓存的值。
  2. 响应使缓存无效的某些其他方法(或方法)。我不知道从提供的代码片段中会有什么。
  3. 过了一段时间。同样,没有更多的背景,我不能说什么时间段是正确的。

1
投票

您可以在构造函数中初始化缓存而不使用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();
    }

}

0
投票

如何扩展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
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.