0

我正在努力解决 Calendar.SelectedDates 是只读的这一事实。我想要一种以编程方式在日历中选择多个日期的方法。我找到了以下代码片段,它完全符合我的要求,但我不明白它为什么会起作用:

protected void Page_Load(object sender, EventArgs e)
{
    SelectedDatesCollection dates = Calendar1.SelectedDates;
    dates.Add(new DateTime(2012, 5, 1));
    dates.Add(new DateTime(2012, 5, 5));
    dates.Add(new DateTime(2012, 5, 9));
}

我所期望的(鉴于我对 C# 的了解有限),完全相反:

protected void Page_Load(object sender, EventArgs e)
{
    ArrayList datelist = new ArrayList();
    datelist.Add(new DateTime(2012, 5, 1));
    datelist.Add(new DateTime(2012, 5, 5));
    datelist.Add(new DateTime(2012, 5, 9));
    Calendar1.SelectedDates = new SelectedDatesCollection(datelist);
}

SelectedDates 只读属性如何受本地 SelectedDatesCollection 变量影响?为什么添加到该局部变量会影响 Calendar1.SelectedDates 属性?

(这只是一个拖放式 ASP.Net 日历,没有更改/特殊属性)

4

3 回答 3

1

SelectedDatesCollection 作为属性是只读的,但您仍然可以将其更改为对象,如添加或删除项目。

更改对象(也就是通过更改数据调用它的方法)和更改对象引用本身、作为类的成员等之间是有区别的。

通过做:

SelectedDatesCollection dates = Calendar1.SelectedDates;

您不是在复制集合,而只是保存它的引用,就像给它取另一个名字一样。

最终,您还可以这样做:

protected void Page_Load(object sender, EventArgs e)
{
    Calendar1.SelectedDates.Add(new DateTime(2012, 5, 1));
    Calendar1.SelectedDates.Add(new DateTime(2012, 5, 5));
    Calendar1.SelectedDates.Add(new DateTime(2012, 5, 9));
}

只是有人会说另一种方式更具可读性。

datesCalendar1.SelectedDates包含对完全相同对象的引用。调用Add方法dates就像调用它一样Calendar1.SelectedDates,反之亦然。

正如您所说,您尝试在第二段代码(不编译)中执行操作时无法重新分配的原因Calendar1.SelectedDates是 SelectedDates 被标记为只读。

将成员标记为只读仅仅意味着它只会被立即分配或在类/结构/等的构造函数中分配。

但是,这并不意味着您不能更改该对象中的数据。

从现在开始,这只是一些额外的信息

有一个类 ReadOnlyCollection,它不允许更改它的 ELEMENTS。

因此,例如,拥有只读 ReadOnlyCollection 将意味着您无法更改成员,也无法更改元素。

例子:

public class Class1
{
    public List<int> Numbers = new List<int> {1, 2, 3};
}

public class Class2
{
    public readonly List<int> Numbers = new List<int> {1, 2, 3};
}

public class Class3
{
    public ReadOnlyCollection<int> Numbers = new ReadOnlyCollection<int> {1, 2, 3};
}

public class Class3
{
    public readonly ReadOnlyCollection<int> Numbers = new ReadOnlyCollection<int> {1, 2, 3};
}

差异:

var c1 = new Class1();
var c2 = new Class2();
var c3 = new Class3();
var c4 = new Class4();

c1.Numbers = new List<int> {4, 5, 6}; // Works
c1.Numbers.Clear(); // Works

c2.Numbers = new List<int> {4, 5, 6}; // Error
c2.Numbers = c2.Numbers; // Error - you just can't reassign it!
c2.Numbers.Clear(); // Works - you are just calling the Clear method of the existing list object.

c3.Numbers = new ReadOnlyCollection<int> {4, 5, 6}; // Works - the member is not readonly

// ReadOnlyCollection doesn't allow to change it's elements after initializing it.
// It doesn't even have these functions:
c3.Numbers.Clear(); // Error
c3.Numbers.Add(); // Error
c3.Numbers.Remove(2); // Error

c4.Numbers = new ReadOnlyCollection<int> {4, 5, 6}; // Error - the member is marked as readonly

// ReadOnlyCollection doesn't allow to change it's elements after initializing it.
// It doesn't even have these functions:
c4.Numbers.Clear(); // Error
c4.Numbers.Add(); // Error
c4.Numbers.Remove(2); // Error
于 2012-05-08T02:06:34.607 回答
1

尝试以下方法 - 请记住,他们还需要能够取消选择日期:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (ViewState["StartDates"] == null)
        {
            startDates = new ArrayList();
        }
        else
        {
            startDates = ViewState["StartDates"] as ArrayList;
        }
    } 

    protected void StartSelection_Changed(Object sender, EventArgs e)
    {
        startCalendar.SelectedDate = new DateTime(startCalendar.SelectedDate.Year,
            startCalendar.SelectedDate.Month, startCalendar.SelectedDate.Day, 0, 0, 0);

        // c.f. http://www.mikepope.com/blog/AddComment.aspx?blogid=604
        int index = -1;
        index = startDates.IndexOf(startCalendar.SelectedDate);
        if (index >= 0)
        {  
            startDates.RemoveAt(index);
        }
        else
        {
            DateTime dateTime 
                = new DateTime(startCalendar.SelectedDate.Year,
                    startCalendar.SelectedDate.Month, 
                        startCalendar.SelectedDate.Day, 0, 0, 0);

            startDates.Add(dateTime);
        }

        ViewState["StartDates"] = startDates;
        DisplaySelectedDates();
    }


    protected void DisplaySelectedDates()
    {
        startCalendar.SelectedDates.Clear();
        for (int i = 0; i < startDates.Count; i++)
        {
            startCalendar.SelectedDates.Add((DateTime)startDates[i]);
        }
    }
于 2012-05-08T02:21:57.847 回答
1

您的问题有一个更简洁的答案:

SelectedDates 只读属性如何受本地 SelectedDatesCollection 变量影响?为什么添加到该局部变量会影响 Calendar1.SelectedDates 属性?

SelectedDatesCollection是引用类型;只读SelectedDates属性返回对该类型对象的引用。因此,局部变量也持有对该对象的引用。因为该对象与 CalendarControl 引用的对象相同,所以对该对象的更改会反映在 CalendarControl 中。


这种情况引发的另一个问题:

这会在设计课程时引起问题吗?

是的!许多开发人员被引诱到错误的安全感中,他们错误地假设,例如,通过只读属性公开列表将阻止消费者代码修改列表。这当然不是真的:通过只读属性公开私有引用类型数据允许您的代码的用户更改对象。 例如:

class Parent
{
    private readonly List<Child> _children = new List<Child>();
    public List<Child> Children { get { return _children; } }
}

class SomeOtherClass
{
    void EvilParentConsumer()
    {
        Parent a = GetSomeParent();
        Parent b = GetSomeParent();

        //Kidnap all of b's children and give them to a!!!
        a.Children.AddRange(b.Children);
        b.Children.Clear();
    }
}

正如 Yorye Nathan 所说,您可以通过将私有列表公开为ReadOnlyCollection. 如果您的要求允许,另一种方法是将集合公开为IEnumerable<T>,它不公开向集合添加元素的方法。

于 2012-05-08T02:59:21.260 回答