【问题标题】:Setting ReadOnly Strings without Reflection设置没有反射的只读字符串
【发布时间】:2016-07-08 13:50:53
【问题描述】:

我正在开发一个需要访问公共字符串的应用程序。 但是,我也希望限制写访问。由于我有很多字符串,我想避免每个字符串都有一个 getter 和 private setter。

见下面的代码:

public readonly string privateMem, peakVM, peakPaged, pagedSysMem, pagedMem, nonpagedSysMem;

    public override void Capture()
    {
        using (Process p = Process.GetCurrentProcess())
        {

            // Throws error as privateMem can't be assigned a value

            privateMem = string.Format("{0:N0} K", p.PrivateMemorySize64 / 1024);
            peakVM = string.Format("{0:N0} K", p.PeakVirtualMemorySize64 / 1024);
            peakPaged = string.Format("{0:N0} K", p.PeakPagedMemorySize64 / 1024);
            pagedSysMem = string.Format("{0:N0} K", p.PagedSystemMemorySize64 / 1024);
            pagedMem = string.Format("{0:N0} K", p.PagedMemorySize64 / 1024);
            nonpagedSysMem = string.Format("{0:N0} K", p.NonpagedSystemMemorySize64 / 1024);
        }

    }    

我知道有一些方法可以通过反射为只读字段分配值,但是我从其他几个 stackoverflow 问题中收集到,这是一个糟糕的主意。

总之:

1。 如何在没有反射的情况下为只读字符串赋值,或者在不使用详细的 getter 和 setter 的情况下是否存在更好的解决方案?

2。 有没有办法声明一个变量,为其赋值,然后将其指定为只读?

【问题讨论】:

  • getter 和 setter 在什么世界里“冗长”?好像你在一个角落里给自己编码
  • 这些字符串是否在运行时设置了多次?那么您将需要{get; private set;} 或选择其他语言。
  • @KeyserSoze 字段和属性之间存在关键区别,尤其是在处理访问权限时。所以如果你打算在 C# 中做这种事情,你需要坚持使用 setter 和 getter。
  • 在您提出这个问题并等待答案的时候,您可能已经完成了一个包含 getter 和 private setter 的干净课程。
  • 这里的大多数人不会称其为“理论”问题,而是“想象”问题..

标签: c# reflection readonly


【解决方案1】:

您尝试做的事情毫无意义。只读字段只能在构造函数中填充。它们对于依赖注入非常有用,例如,事物只在构造函数内部设置,仅此而已,不能在它之外进行修改。

例如,您始终可以使用私有集。说 getter 和 setter 是“冗长的”也是没有意义的。他们在那里是有原因的。试图找到一种以不同方式做事的方法很可能意味着疯狂的代码,而这一切只是为了避免使用简单的 setter?

想想你在做什么以及为什么,然后最后问自己,这真的值得吗?

【讨论】:

    【解决方案2】:

    您当前使用字典的答案不正确,因为它不是只读的。

    更正确的方法是基于字典初始化 IReadOnlyDictionary(在 .NET 4.5 或更高版本中)实例一次,并添加逻辑以防止它在初始化后被新值覆盖。

    通过使用支持字段,我们可以确保即使在类内部尝试修改它,一旦它包含值就不会被覆盖。

    private IReadOnlyDictionary<string, string> _memoryStrings;
    public IReadOnlyDictionary<string, string> memoryStrings
    {
        get
        {
            return _memoryStrings;
        }
        private set
        {
            if (_memoryStrings != null && _memoryStrings.Count() > 0) return;
            _memoryStrings = value;
        }
    }
    
    public void Capture()
    {
        if (memoryStrings == null || memoryStrings.Count() == 0)
        {
            using (Process p = Process.GetCurrentProcess())
            {
                memoryStrings = new Dictionary<string, string>
                {
                    {"privateMem", string.Format("{0:N0} K", p.PrivateMemorySize64 / 1024)},
                    {"peakVm", string.Format("{0:N0} K", p.PeakVirtualMemorySize64 / 1024)},
                    {"peakPaged", string.Format("{0:N0} K", p.PeakPagedMemorySize64 / 1024)},
                    {"pagedSysMem", string.Format("{0:N0} K", p.PagedSystemMemorySize64 / 1024)},
                    {"pagedMem", string.Format("{0:N0} K", p.PagedMemorySize64 / 1024)},
                    { "nonpagedSysMem", string.Format("{0:N0} K", p.NonpagedSystemMemorySize64 / 1024)}
                };
            }
        }
    }
    

    这为我们提供了多个级别的不变性。

    如果您多次调用Capture(),它只会在第一次运行计算。

    如果您尝试在类中的其他位置覆盖字典,一旦它包含值就不会被覆盖。

    如果您在创建集合后尝试覆盖属性或添加值,您将收到编译时异常(在使用索引器或尝试使用 .Add() 方法的情况下)或运行时异常(在通过转换为IDictionary 使用.Add() 方法的情况下)。

    【讨论】:

    • 精彩的解释和实现,谢谢!
    【解决方案3】:

    我最终只使用了字典。这样我只需要一个 getter 和一个私有 setter,而且以后添加更多字符串/系统信息时它的可扩展性要大得多。

    public Dictionary<string, string> memoryStrings
             {
                 get;
                 private set;
             }
    
            public MemoryUsageFragment() {
                memoryStrings = new Dictionary<string, string>();
            }
    
            public override void Capture()
            {
                using (Process p = Process.GetCurrentProcess())
                {
                    memoryStrings.Add("privateMem", string.Format("{0:N0} K", p.PrivateMemorySize64 / 1024));
                    memoryStrings.Add("peakVm", string.Format("{0:N0} K", p.PeakVirtualMemorySize64 / 1024));
                    memoryStrings.Add("peakPaged", string.Format("{0:N0} K", p.PeakPagedMemorySize64 / 1024));
                    memoryStrings.Add("pagedSysMem", string.Format("{0:N0} K", p.PagedSystemMemorySize64 / 1024));
                    memoryStrings.Add("pagedMem", string.Format("{0:N0} K", p.PagedMemorySize64 / 1024));
                    memoryStrings.Add("nonpagedSysMem", string.Format("{0:N0} K", p.NonpagedSystemMemorySize64 / 1024));
                }    
            }
    

    【讨论】:

    • private set 只允许对字典进行私有设置。用户仍然可以公开添加/删除字典。字符串不是只读的。您可以创建一个根据传入的键返回字符串的方法,并使字典成为私有字段。
    • 非常有效的观点。并且该方法的想法可行,但是我将 WPF ListBox 绑定到 memoryStrings,并希望尽可能保留该功能
    • @KeyserSoze 请参阅我的回答,了解如何解决当前字典实施的问题
    【解决方案4】:
    public string privateMem        { get; private set; }
    public string peakVM            { get; private set; }
    public string peakPaged         { get; private set; }
    public string pagedSysMem       { get; private set; }
    public string pagedMem          { get; private set; }
    public string nonpagedSysMem    { get; private set; }
    

    这总共花了大约 20 秒。这有什么好说的?

    【讨论】:

    • 这真的是答案吗?
    • 当然是。不是我的错,他的问题有缺陷。但是这段代码将解决他的问题,而不会使他的代码不必要地复杂化。
    • 从他的cmets可以看出,他知道怎么写属性。他实在是太固执了。所以基本上你的答案只是显示你可以多快编写代码/属性。这并没有增加任何有用的东西。 -1.
    • 随心所欲地投反对票,但目的是回答他的问题。没有更好的方法,正如安德烈在回答中指出的那样,他试图做的事情甚至没有意义。
    • @Sylverac,我了解 getter 和 setter,并且我知道考虑到当前字符串的数量,它们是处理此问题的实用方法。但考虑到将来,我想添加更多字符串并获取更多系统信息以显示到屏幕上,甚至用 wpf 很好地格式化它们。每个字符串上的 getter 和 setter 会变得很麻烦。更类似于集合的东西可能会提供更好的功能,我不会认为这种不必要的复杂性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    • 2023-03-09
    • 2021-03-27
    • 2023-03-07
    • 2010-11-08
    相关资源
    最近更新 更多