【问题标题】:C# property, is it possible to get around defining get without defining set (no backing variable)?C# 属性,是否可以绕过定义 get 而不定义 set(没有支持变量)?
【发布时间】:2009-12-01 23:06:11
【问题描述】:

假设您有一个具有 300 个属性且没有后备变量的类,这些属性中的每一个都返回一个小数/双精度值。

例子:

public decimal MathValue { get; set; }

现在您决定对这些值中的每一个进行四舍五入。

我正在寻找最简单的方法来重构它,而不必重写所有这些属性。

这个等效的东西实际上有效:D:

public decimal MathValue { get {return Math.Round(MathValue);} set; }

【问题讨论】:

  • 您可能想要考虑稍后再回到这个类,并将一些属性重新分解为漂亮干净的类,这样您就可以避免这样的混乱。
  • 大声笑,我完全夸大了我只是好奇有人怎么能绕过这样的事情:)

标签: c# properties


【解决方案1】:

没有。如果您在 getter 或 setter 中需要任何自定义逻辑,则不能使用自动属性。

【讨论】:

  • 如果不为每个变量声明一个变量,绝对没有办法覆盖这个逻辑或解决方案?
  • 另一种方法是编写一个命令行工具(或者最好使用 T4)从其他文件/数据为您生成 300 个属性。
  • 您可以使用 PostSharp 之类的东西在编译后重写您的属性。然而,对于如此微不足道的事情可能不值得。
  • 我认为这更像是一个理论问题,而不是一个蛮力问题......我愿意接受任何可以帮助我以最少的代码解决问题的想法。
  • 我不明白这如何回答他的问题。
【解决方案2】:

您可以创建一个伪装成小数但返回四舍五入值的新值类型。像这样的:

struct RoundedDecimal
{
    public decimal Value { get; private set; }

    public RoundedDecimal(decimal value) : this()
    {
        this.Value = value;
    }

    public static implicit operator decimal(RoundedDecimal d)
    {
        return Math.Round(d.Value);
    }
}

类中的每个属性都应该是RoundedDecimal 类型而不是decimal

【讨论】:

  • 我称之为Int96,因为它实际上就是这样。
  • 这是在不破坏界面的情况下完成它的好方法。
  • 实际上,如果您创建一个名为 Value 的公共只读字段并在构造函数中对其进行舍入,可能会更好。
  • 到目前为止,我最喜欢这个解决方案。 :)
  • 这也很容易实现。 ctrl-h "十进制" "RoundedDecimal"
【解决方案3】:

重构代码的最简单方法是什么?这是我要做的:

  1. 打开记事本++(没有就拿)
  2. 将类的所有属性复制/粘贴到空白文本区域。
  3. 将光标放在第一行的开头: 公共小数 MathValue1 { 得到;放; }
  4. 开始录制宏(点击工具栏上的录制按钮)
  5. 按住 ctrl+向右箭头(称为“word right”)3 次,将光标置于属性名称的开头。
  6. 执行 shift+ctrl+右箭头 1 次并执行 Copy 以将属性名称放入剪贴板
  7. 再右击 3 次将光标放在“get”之后
  8. 删除 get 后的分号并开始输入 " { return Math.Round(_"
  9. 粘贴 10 输入");}"
  10. 再右击 2 次,将光标放在“set”之后
  11. 删除集合后的分号并开始输入“{_”
  12. 粘贴
  13. 类型“=值;}
  14. 按 End 键将光标移至行尾
  15. 按右箭头键将光标移至下一行的开头。
  16. 按停止按钮结束宏(工具栏上的方形按钮)
  17. 点击“多次运行宏”按钮(工具栏上的双箭头图标)并说“运行直到文件结束”
  18. 将生成的文本复制/粘贴回您的类中以替换原始属性定义。

现在您需要定义一组相应的私有变量,这些私有变量以下划线开头,但与属性名称相同。从您的类中的新属性副本开始,然后执行与上述类似的一组步骤。

我的假设是每行以 2 个选项卡开头,并且属性之间没有空行。

与其让每个属性都调用 Math.Round,不如考虑定义自己的实用函数,它们都调用,这样如果需要再次更改,只需在一个地方更改即可。

【讨论】:

  • +1 - 永远不会想到这一点。他可能想在他这样做之前按 ctrl-k-d 以便 VS 清理所有空白。
【解决方案4】:

您可以创建此类的派生类,它会覆盖获取并返回四舍五入的值。然后,您需要将基本属性修改为虚拟。但这将允许您在不定义集合和使用自动属性的情况下定义 get。

public class Base
{
    public virtual decimal MathValue { get; set; }
}

public class Derived : Base
{
    public override decimal MathValue
    {
        get { return Math.Round(base.MathValue); }
    }
}

【讨论】:

  • 如何做到这一点?如果可能的话,我想自己覆盖获取。你能给我举个例子吗?
  • 当然。我还澄清了整个属性需要是虚拟的,而不仅仅是 getter。
  • 继承似乎有点矫枉过正。
  • 严格考虑“是否可以在不定义 set 的情况下绕过定义 get”这个问题,我不同意。从更广泛的角度考虑他的整体情况,这种方法也为他提供了一个圆形和非圆形的课程版本。然后,生成的类名可以具有更好的语义。但是,我同意除非他能获得这些特殊的好处,否则这可能是矫枉过正。
【解决方案5】:

但是如果客户端不想要一个四舍五入的值怎么办?即,一些新的客户端代码设置了一个小数并期望返回“精确”值?

如果某些客户端确实需要对属性调用的输出进行四舍五入,则客户端应处理此问题并让您的类独自一人。

【讨论】:

  • 那么在这种情况下,对于那个特定的属性,get 可以单独处理:)...我唯一想做的就是避免为每个属性声明一个变量。
【解决方案6】:

Visual Studio 曾经有一个内置的“prop”代码 sn-p,它会生成类似于以下代码的内容:

    private decimal _MathValue;

    public decimal MathValue
    {
        get { return _MathValue; }
        set { _MathValue = value; }
    }

这将使您获得完整的解决方案,但从 VS 2008 开始,它现在会生成自动属性版本:

    public decimal MathValue { get; set; }

我还没试过,但是here is a suggestion for creating your own code snippet to get the VS 2005 version of the "prop" code snippet back

【讨论】:

  • 我更感兴趣的是绕过代码而不是手动重构类中的所有内容,即使我有一个为我做的 sn-p。
  • 啊,是的,我应该更仔细地阅读您的问题。我看到您的情况已经定义了数百个属性,因此这对您并没有太大帮助。
【解决方案7】:

一种选择是使用面向方面的编程来拦截返回时的属性调用,并在将控制权返回给调用者之前对返回值进行四舍五入。

【讨论】:

    【解决方案8】:

    您可以使用PostSharp 或其他一些基于 .NET 的 AOP 框架来完成此操作。这是MethodExecutionEventArgs.ReturnValue property,它说它可以用来“修改返回值......”

    这样就可以了:

    [Serializable]
    public class RoundingAttribute : OnMethodBoundaryAspect
    {
        public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
            base.OnExit(eventArgs);
            eventArgs.ReturnValue = Math.Round((double)eventArgs.ReturnValue, 2);
        }
    }
    
    class MyClass
    {
        public double NotRounded { get; set; }
    
        public double Rounded { [Rounding] get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var c = new MyClass
                    {
                        Rounded = 1.99999, 
                        NotRounded = 1.99999
                    };
    
            Console.WriteLine("Rounded = {0}", c.Rounded); // Writes 2
            Console.WriteLine("Not Rounded = {0}", c.NotRounded);  // Writes 1.99999
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-04-21
      • 2020-06-21
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      相关资源
      最近更新 更多