Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在依赖于同一类的其他属性的属性中强制执行适当值的公认方法是什么?_C#_.net_Oop_Properties - Fatal编程技术网

C# 在依赖于同一类的其他属性的属性中强制执行适当值的公认方法是什么?

C# 在依赖于同一类的其他属性的属性中强制执行适当值的公认方法是什么?,c#,.net,oop,properties,C#,.net,Oop,Properties,假设我有一个类,它有三个属性,如下所示 public class Travel { public int MinAirportArrival { get; set; } public int MinFlightTime { get; set; } public int TotalTravelTime { get; set; } } TotalTravelTime必须至少为MinairPortarival和MinFlightTime之和,但如果中途停留或类似情况,也可以为更多 我很清

假设我有一个类,它有三个属性,如下所示

public class Travel
{
  public int MinAirportArrival { get; set; }
  public int MinFlightTime { get; set; }
  public int TotalTravelTime { get; set; }
}
TotalTravelTime必须至少为MinairPortarival和MinFlightTime之和,但如果中途停留或类似情况,也可以为更多

我很清楚,我可以将逻辑放在TotalTravelTime的setter中

我的问题是关于MinFlightTime和MinairPortarival的更改。期望TotalTravelTime首先增加是正确的吗?如果不是,当其他人中的一个将使总和大于TotalTravelTime时抛出一个异常

我还有什么其他选择可以合理地控制这种情况

我应该让负责保存状态的对象检查类上的有效属性吗?我可能还有其他的逻辑

编辑

我不会在任何地方存储额外的时间,如果有的话,所以这不仅仅是一个添加一些属性的问题。只是想澄清一下,这个类只是我面临的问题的一个人为的例子,但我认为它与这个问题非常吻合。

因为TotalTravelTime是从另外两个类派生出来的,所以我不会为它实现公共setter,而是由其他两个属性的setter更新的私有属性。

框架设计指南建议属性不应相互依赖。 应始终可以按任意顺序设置多个属性的值

以下是要查看的选项:

  • 在这种情况下:

    public class Travel
    {
      public int MinAirportArrival { get; set; }
      public int MinFlightTime { get; set; }
      public int AdditionalTravelTime { get; set; }
      public int TotalTravelTime
      {
        get { return MinAirportArrival + MinFlightTime + AdditionalTravelTime; }
      }
    }
    
    这利用了这样一种效果,即可以从三个其他值中重构总旅行时间,而这三个值可以独立设置,而不需要依赖项

  • 一般情况下的解决方案是接受任何值,并在实例发送到存储器时验证这些值

    public class Travel
    {
      public int MinAirportArrival { get; set; }
      public int MinFlightTime { get; set; }
      public int TravelTime { get; set; }
      public void Save()
      {
        // validate TravelTime > MinAirportArrival + MinFlightTime 
      }
    }
    
  • 另一个选项是将属性设置为只读,并提供一种方法来批量更新属性值

    public class Travel
    {
      public int MinAirportArrival { get; private set; }
      public int MinFlightTime { get; private set; }
      public int TravelTime { get; private set; }
      public void UpdateTimes(
        int minAirportArrival, int minFlightTime, int travelTime)
      {
        // validate travelTime > minAirportArrival + minFlightTime 
        MinAirportArrival = minAirportArrival;
        MinFlightTime = minFlightTime;
        TravelTime = travelTime;
      }
    }
    
  • 或者,您可以使Travel对象不可变,并使用构造函数、工厂方法或可变生成器对象来创建实例

    建造商:

    public class Travel
    {
      public Travel(int minAirportArrival, int minFlightTime, int travelTime)
      {
        // validate travelTime > minAirportArrival + minFlightTime 
      }
      public int MinAirportArrival { get; }
      public int MinFlightTime { get; }
      public int TravelTime { get; }
    }
    
    工厂方法:

    public class Travel
    {
      public static Travel CreateTravel(
        int minAirportArrival, int minFlightTime, int travelTime)
      {
        // validate travelTime > minAirportArrival + minFlightTime 
        return new Travel(minAirportArrival, minFlightTime, travelTime);
      }
      private Travel(int minAirportArrival, int minFlightTime, int travelTime);
      public int MinAirportArrival { get; }
      public int MinFlightTime { get; }
      public int TravelTime { get; }
    }
    
    建造商类别:

    public class TravelBuilder
    {
      public int MinAirportArrival { get; set; }
      public int MinFlightTime { get; set; }
      public int TravelTime { get; set; }
      public Travel BuildTravel()
      {
        // validate TravelTime > MinAirportArrival + MinFlightTime 
        return new Travel(MinAirportArrival, MinFlightTime, TravelTime);
      }
    }
    
    这三个选项的示例都可以在.NET framework中找到


  • 我认为最好将您的自定义验证逻辑放入一个私有助手方法中,然后从每个属性设置器调用此方法,如下所示:

    public class Travel
    {
        private int minAirportArrival;
        private int minFlightTime;
        private int additionalTravelTime;
    
        public int MinAirportArrival
        { 
            get
            {
                return this.minAirportArrival;
            }
            set
            {
                ThrowOnImpossibleTimes(value, this.minFlightTime, this.additionalTravelTime);
                this.minAirportArrival = value;
            }
        }
    
        // other properties...
    
        private static void ThrowOnImpossibleTimes(int minAirportArrival, int minFlightTime, int additionalTravelTime)
        {
            // do check and eventually throw...
        }
    }
    

    一种方法是实现一种验证方法来检查条件。该方法将从每个属性的setter调用,并在不满足条件时抛出异常,以允许以任何顺序设置属性,但保留已建立的验证API(由某些API自动使用,包括
    DataGridView
    和ASP.NET MVC验证),其中一个选项是
    IDataErrorInfo
    。这不是实现的最简单接口,但它是可行的(见下文)。您可以将验证概括为:

    IDataErrorInfo dei = obj as IDataErrorInfo;
    if(dei != null) {
        string err = dei.Error;
        if(!string.IsNullOrEmpty(err)) throw new SomeTypeOfException(err);
    }
    
    下面是一个实现:

    public class Travel : IDataErrorInfo
    {
        public int MinAirportArrival { get; set; }
        public int MinFlightTime { get; set; }
        public int TotalTravelTime { get; set; }
        string IDataErrorInfo.this[string property] {
            get {
                switch (property) {
                    case "TotalTravelTime":
                        if (TotalTravelTime < MinAirportArrival + MinFlightTime) {
                            return "not enough time blah";
                        }
                        break;
                }
                return "";
            }
        }
        string IDataErrorInfo.Error {
            get {
                StringBuilder sb = new StringBuilder();
                AppendError(ref sb, "MinAirportArrival");
                AppendError(ref sb, "MinFlightTime");
                AppendError(ref sb, "TotalTravelTime");
                return sb == null ? "" : sb.ToString();
            }
        }
        void AppendError(ref StringBuilder builder, string propertyName) {
            string error = ((IDataErrorInfo)this)[propertyName];
            if (!string.IsNullOrEmpty(error)) {
                if (builder == null) {
                    builder = new StringBuilder(error);
                } else {
                    builder.Append(error);
                }
            }
        }
    }
    
    公务舱旅行:IDataErrorInfo
    {
    public int minairportarival{get;set;}
    public int MinFlightTime{get;set;}
    公共整数TotalTravelTime{get;set;}
    字符串IDataErrorInfo。此[string属性]{
    得到{
    交换机(属性){
    案例“总旅行时间”:
    if(总旅行时间
    它不是完全派生的。它必须是总和的最小值。额外的旅行时间可以是各种各样的东西,并且没有被存储。但是总旅行时间存储在您的代码中。我的建议是,不要存储三个
    (minairportarival、MinFlightTime、TotalTravelTime)
    ,而是存储表示完全相同数据的
    (minairportarival、MinFlightTime、AdditionalTravelTime)
    ,只是与已知的
    TotalTravelTime
    不同,而不是
    TotalTravelTime
    本身。