封装一个私有常量

3

我在想如果我有一个常量属性,我能不能设置一个公共属性来封装它呢?

例如:

private const int DEFAULT_CHARGE = 200;
public int default_charge
{
     get { return DEFAULT_CHARGE; }
}

我没有收到任何错误,但如果某些东西是常量,我真的不理解为什么要封装它。我真的想了解为什么这样做:

  1. 对/错
  2. 为什么要这样做?
  3. 好处是什么?

4
一个非常重要的原因是编译器可以(而且会)在编译时展开常量。换句话说,改变值并分发新的汇编代码不会使客户端实际上使用新的值,除非它们被重新编译。有时候这是你想要的,但通常不是(还有一些时候并不重要,因为你不会替换单个程序集)。 - Jeroen Mostert
5个回答

2

重要的是要知道常量会被编译器内联。这意味着以下两个示例将得到相同的程序:

private const int DEFAULT_CHARGE = 200;
public int default_charge
{
     get { return DEFAULT_CHARGE; }
}

编译后,这与此相同:
public int default_charge
{
     get { return 200; }
}

如您所见,在编译后对DEFAULT_CHARGE的引用已经丢失。在需要在多个项目中重复使用相同常量的解决方案中,这一点非常重要。

假设您将DEFAULT_CHARGE作为public const分别在LibA和LibB中使用。您的项目经理告诉您把值从200改为300。直觉地,您会去到定义了DEFAULT_CHARGE的LibA,将其更改为300,然后重新编译和部署LibA。

结果是,现在LibA使用新值300而LibB仍使用旧值200,因为该常量在编译时被写入DLL中。

封装常量的原因是使您更轻松地更改其值。需求随时间而变化。今天是一个常数的DEFAULT_CHARGE值可能在未来被替换为配置值。

通过封装常量,您还可以防止刚才我解释的问题。如果LibA、LibB(以及LibX、LibY、LibZ等)都依赖于封装,则只需要重新编译和部署LibA即可设置所有相关程序的默认费用。


这是一个非常清晰的解释,非常感谢! - lonewolf2288

1
即使某些内容是常量,您仍可能希望控制其返回给调用者的方式。如果您有一个存储到小数点后5位的Double变量,您可能希望将该值以2个小数点的精度返回给调用者。封装可以帮助您在字段上获得此控制权。
因此,您可能需要像这样封装一个常量字段。
private const double DEFAULT_CHARGE = 200.12345;
   public int default_charge
 {
 get { return Math.round(DEFAULT_CHARGE,2); }
 }

1
你可能想要实现接口(interface)、抽象类(abstract class)等相关内容:
   public interface IChargable {
     int default_charge {get;}
   }

   public class MySimpleChargable: IChargable {
     private const int DEFAULT_CHARGE = 200;

     public int default_charge {get { return DEFAULT_CHARGE; }} 
   } 

你可以实现这样一个结构作为一个“桩”:
开始:
   // Version 1.0
   public class MyChargable {
     private const int DEFAULT_CHARGE = 200;

     //TODO: implement (rare) "some condition" case
     public int default_charge {get { return DEFAULT_CHARGE; }} 
   } 

稍后:
   // Version 1.1
   public class MyChargable {
     private const int DEFAULT_CHARGE = 200;

     public int default_charge {
       get { 
         if (some condition)
           return SomeComputation();

         return DEFAULT_CHARGE; 
       }
     }  

非常感谢!真的帮了大忙。 - lonewolf2288

0
据我所知,您可以轻松创建公共访问器以访问私有常量。
当您需要添加额外的逻辑到getter中时,例如以某种方式格式化返回值或者在应用某个条件时返回其他值时,您应该创建一个常量访问器。
如果您的常量是作为库用于其他命名空间的一部分,则需要创建一个访问器。因为如果您编译包含该常量的命名空间,依赖的命名空间将仅记住常量的旧值,而不是新值。

0

这取决于你的需求。
一般来说,属性旨在封装任何您想要的获取/设置逻辑,甚至是可计算的值或常量。
在我看来,公共属性返回私有常量的值在架构上是很好的,这种情况下使用您代码的人根本不知道他正在处理常量,即他从您的实现中抽象出来,这是很好的。如果您决定不将其作为常量而将其作为可配置值,则不会向库使用者公开它,只需将其设置为:

// private const int DEFAULT_CHARGE = 200; <-- is not used anymore

public int DefaultCharge
{
    get { return SomeSortOfFileOrDatabaseConfiguration.DefaultCharge; }
}

或者甚至更好

public int DefaultCharge
{
    get
    {
        return CurrentUser.PersonalSettings.DefaultCharge; 
    }
}

这段代码依赖于用户的个人设置,而不向客户端提供任何信息。这是其主要优点。它完全关乎封装和抽象。
但请使用适当的命名属性 - 应该是 public int DefaultCharge,并注意 Jeroen Mostert 在常量内联方面的评论。

你们是最棒的,非常感谢!我现在觉得我理解了它背后的理论和原理!真的希望有一天我能达到你们的水平! :) - lonewolf2288

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