C#属性,是否可以在不定义set(没有备份变量)的情况下避免定义get?

7
假设您有一个类,其中包含300个属性,没有备份变量,每个属性返回一个十进制/双精度浮点数。
例如:
public decimal MathValue { get; set; }

现在你决定将这些值中的每一个都四舍五入。

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

以下是等效且实际可行的代码:

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

你可能想要考虑稍后回到这个类并将一些属性重构为漂亮干净的类,以避免出现这样的混乱。 - ChaosPandion
哈哈,我有点夸张了,其实我只是好奇有人怎么能绕过这种事情 :) - Andrew
8个回答

6

不可以。如果你需要在getter或setter中添加任何自定义逻辑,就不能使用自动属性。


有没有办法在不为每个变量声明一个变量的情况下覆盖此逻辑或解决方案? - Andrew
另一种选择是编写一个命令行工具(或最好使用T4),从其他文件/数据中为您生成300个属性。 - ConsultUtah
你可以使用类似PostSharp的工具在编译后重写属性。然而,对于像这样微不足道的事情可能并不值得。 - Pavel Minaev
我认为这更像是一个理论问题而不是一个暴力解决的问题……我乐于接受任何能帮助我用最少的代码解决问题的想法。 - Andrew
2
我不明白这如何回答他的问题。 - ChaosPandion

5
您可以创建一个新的值类型,假装它是十进制数,但返回四舍五入的值。像这样:

您可以创建一个新的值类型,假装它是十进制数,但返回四舍五入的值。像这样:

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,因为这就是它的实际名称。 - Pavel Minaev
这是一个很好的方法,可以在不破坏接口的情况下完成它。 - ChaosPandion
到目前为止,我最喜欢这个解决方案了。 :) - Andrew
这也很容易实现。Ctrl-H "decimal" "RoundedDecimal" - ChaosPandion
昨天我没有编译我的代码,但今天我编译了并且出现了错误。通过调用默认的无参数构造函数来解决了这个问题。我会在我的答案中更新代码。 - Andy
显示剩余3条评论

5
最简单的重构代码的方法?以下是我的做法:
  1. 打开Notepad++(如果没有请下载)
  2. 将类的所有属性复制/粘贴到一个空白文本区域中。
  3. 将光标放在第一行的开头: public decimal MathValue1 { get; set; }
  4. 开始录制宏(单击工具栏上的录制按钮)
  5. 按住ctrl+右箭头(称为“向右单词”)3次,将光标放在属性名称的开头。
  6. 按shift+ctrl+右箭头1次并复制以将属性名称放入剪贴板
  7. 再向右移动3个单词,将光标放在“get”之后
  8. 删除get后的分号并开始输入“{ return Math.Round(_”
  9. 粘贴
  10. 输入“); }”
  11. 再向右移动2个单词,将光标放在“set”之后
  12. 删除set后的分号并开始输入“{ _”
  13. 粘贴
  14. 输入“= value; }
  15. 按End键将光标移到行尾
  16. 按右箭头键将光标移到下一行的开头。
  17. 按停止按钮结束宏的录制(工具栏上的方形按钮)
  18. 单击“多次运行宏”按钮(工具栏上的双箭头图标)并说“运行到文件结尾”
  19. 将结果文本复制/粘贴回类中以替换原始属性定义。

现在,您需要定义一组相应的私有变量,它们以下划线开头,但其余名称与属性相同。从类中获取属性的新副本,并执行与上述描述类似的一组步骤。

我的假设是每行都以2个制表符开头,并且属性之间没有空行。

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


+1 - 从来没有想过这一点。他在执行此操作之前可能需要按下 ctrl-k-d,这样 VS 就会清除所有空格。 - ChaosPandion

1
你可以创建一个派生类,覆盖 gets 方法并返回舍入后的值。然后你需要将基类属性修改为虚拟的。这样就可以定义 get 而不定义 set 并使用自动属性了。
public class Base
{
    public virtual decimal MathValue { get; set; }
}

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

这怎么能实现呢?如果可能的话,我想覆盖gets本身。你可以给我一个例子吗? - Andrew
当然。我还澄清了整个属性都需要是虚拟的,不仅仅是getter方法。 - G-Wiz
继承似乎对此来说有些过头了。 - ChaosPandion
严格来说,“是否可以在不定义set的情况下绕过定义get”的问题,我不同意。从更广泛的角度来看他的整体场景,这种方法也为他提供了一个有圆角和无圆角版本的类。由此产生的类名可以具有更好的语义。然而,我同意,除非他能获得这些特定的好处,否则这可能是过度设计。 - G-Wiz

0

Visual Studio曾经有一个内置的“prop”代码片段,可以生成类似以下代码:

    private decimal _MathValue;

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

这样可以让你向完整的解决方案迈进一大步,但自从VS 2008以来,它现在生成自动属性版本:

    public decimal MathValue { get; set; }

我还没有尝试过这个,但这里有一个建议,可以创建自己的代码片段来获取“prop”代码片段的VS 2005版本


我更感兴趣的是绕过代码的方法,而不是手动重构类中的所有内容,即使我有一个可以为我完成这项工作的代码片段。 - Andrew
啊,是的,我应该更仔细地阅读你的问题。我看到你已经定义了数百个属性,所以这并没有对你有太大帮助。 - Dr. Wily's Apprentice

0
你可以使用 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
    }
}

0

但是如果客户端不想要一个四舍五入的值会发生什么呢?例如,一些新的客户端代码设置了一个小数,并期望得到“精确”的值?

如果一些客户端确实需要属性调用的输出被四舍五入,那么客户端应该处理这个问题,让你的类保持原样。


那么在这种情况下,对于该特定属性,get方法可以保持不变 :) ...我想做的唯一一件事就是避免为每个属性声明一个变量。 - Andrew

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接