簡體   English   中英

兩個日期作為C#中的屬性

[英]Two dates as properties in C#

我想要兩個簡單的屬性:開始日期和結束日期。 我想設置一個約束條件,即開始日期必須在結束日期之前。 修改兩個值時會出現問題 - 它們可能(一起!)組成一個新的正確對,但在添加它們時,會出現錯誤。 簡單的例子:

start = 5;
end = 10;

new_start = 20;
new_end = 30;

start = new_start; // error!
end = new_end;

這就是我介紹第三個屬性的原因。 問題是 - 代碼看起來很糟糕(尤其是.Item1,.Item2)。

有沒有辦法在C#中以更好的方式做到這一點?

    private DateTime start;
    private DateTime end;

    public DateTime Start { get { return start; } }
    public DateTime End { get { return end; } }


    public Tuple<DateTime, DateTime> Dates
    {
        get
        {
            return new Tuple<DateTime, DateTime>(Start, End);
        }
        set
        {
            if (value.Item1 <= value.Item2)
            {
                start = value.Item1;
                end = value.Item2;
            }
            else
                throw new InvalidDates();
        }
    }

好吧,你可能會考慮一個代表開始/結束組合的類型是否有意義。 一種... Interval (是的,這是一個不那么微妙的插頭野田的時間 ,這使日期/時間一般都比較好處理無妨。但是你可以創建自己的Interval結構的DateTime ,如果你想...),那么你可以有一個單一的財產以確保有效的方式組合開始/結束時間。

但如果你真的想把它們作為單獨的屬性,我會選擇一個簡單的setter。 我不會為此創建一個單獨的異常 - 只需使用ArgumentException

public void SetDates(DateTime start, DateTime end)
{
    if (end < start)
    {
        throw new ArgumentException("End must be at or after start", "end");
    }
    this.start = start;
    this.end = end;
}

哇。 很多答案。 好吧,這是我的看法。

為開始日期和結束日期創建兩個公共屬性,然后添加執行驗證的SetStartAndEndDates方法。 公共財產應該有私人制定者。 由於如果設置了無效日期, SetStartAndEndDates方法將引發錯誤,您將需要創建允許您測試潛在日期的方法。 為了說明這種方法,我將創建一個虛構的CalendarEvent類:

public class CalendarEvent
{
    public DateTime StartDate { get; private set; }
    public DateTime EndDate { get; private set; }

    public SetStartAndEndDates(DateTime start, DateTime end)
    {
        if (start <= end)
        {
            StartDate = start;
            EndDate = end;
        }
        else
        {
            throw new InvalidDates();
        }
    }

    public bool IsValidEndDate(DateTime end)
    {
        return StartDate <= end;
    }

    public bool IsValidStartDate(DateTime start)
    {
        return start <= EndDate;
    }

    public bool IsValidStartAndEndDate(DateTime start, DateTime end)
    {
        return start <= end;
    }
}

並使用它而不拋出異常:

var event = new Event();
var start = DateTime.Now;
var end = start.AddDays(7);

if (event.IsValidStartAndEndDate(start, end))
{
    event.SetStartAndEndDates(start, end);
}

使用方法將它們組合在一起:

public void SetDates(DateTime start, DateTime end)
{
    if(start >= end)
        throw new ArgumentException("start must be before end");
    this.start = start;
    this.end = end;
}

使用方法作為setter:

public void SetDates(DateTime startDate, EndDate endDate)
{
    if (startDate <= endDate)
    {
        start = startDate;
        end = endDate;
    }
    else
    {
        throw new InvalidDates();
    }
}

我沒有使用兩個基礎DateTime ,而是編寫一個類,其中包含一個DateTime用於開始,一個TimeSpan用於開始和結束之間的差異。 開始的setter只會改變DateTime ,而end的setter只會改變TimeSpan (如果它會使TimeSpan負,則給出例外)。

我記得,你已經在Google日歷和Outlook的日歷中看到了這樣的行為。 更改事件的開始時間也會更改結束時間,但會保持持續時間不變。

public class TimeWindow
{
    private TimeSpan duration;

    public DateTime StartTime { get; set; }

    public DateTime EndTime
    {
        get
        {
            return this.StartTime.Add(this.duration);
        }

        set
        {
            // this will throw a ArgumentOutOfRangeException if value is smaller than StartTime
            this.duration = value.Subtract(this.StartTime);
        }
    }

    public void SetStartAndEnd(DateTime start, DateTime end)
    {
        this.StartTime = start;
        this.EndTime = end;
    }
}

OO封裝並不總是關於漂亮的實現,它通常是關於提供一致的“黑盒子”行為的漂亮接口。 如果編寫“看起來很糟糕”的代碼提供了與設計一致的行為的流暢界面,那么最重要的是什么? 我認為你所擁有的解決方案完全有效,可以保持班級內部的一致性。

從設置器中取出驗證檢查並添加驗證方法。 然后,您可以毫無例外地以任何順序設置它們,並在您認為通過調用validate之后檢查最終結果。

private DateTime start;
private DateTime end;

public DateTime Start { get { return start; } }
public DateTime End { get { return end; } }

public void Validate()
{
    if (end.Ticks < start.Ticks)
        throw new InvalidDates();
}

答案中未列出的另一種方法是實現ISupportInitialize接口。 在修改日期之前,您調用BeginInit方法,然后調用 - EndInit EndInit驗證日期。

public class SomeClass : ISupportInitialize
{
    private bool initializing;
    private DateTime start;
    private DateTime end;

    public DateTime Start
    {
        get { return start; }
        set { CheckInitializing(); start = value; }
    }

    public DateTime End
    {
        get { return end; }
        set { CheckInitializing(); end = value; }
    }

    private void CheckInitializing()
    {
        if (!initializing)
        { throw new InvalidOperationException("Can not execute this operation outside a BeginInit/EndInit block."); }
    }

    public void BeginInit()
    {
        if (initializing)
        { throw new InvalidOperationException("Can not have nested BeginInit calls on the same instance."); }

        initializing = true;
    }

    public void EndInit()
    {
        if (!initializing)
        { throw new InvalidOperationException("Can not call EndInit without a matching BeginInit call."); }

        if (start <= end)
        { throw new InvalidDates(); }

        initializing = false;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM