[英]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.