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

あなたの質問にはもっと簡潔な答えがあります:

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 に答える
1

プロパティとしての SelectedDatesCollection は ReadOnly ですが、項目の追加または削除のようにオブジェクトとして変更できます。

オブジェクトの変更 (別名、データを変更してメソッドを呼び出す) と、オブジェクト参照自体をクラスのメンバーとして変更することなどには違いがあります。

行うことによって:

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メソッドを onで呼び出すのは、 ondatesで呼び出すのと同じCalendar1.SelectedDatesであり、その逆も同様です。

Calendar1.SelectedDates2 番目のコード (コンパイルされない) で実行しようとしたときに再割り当てできなかった理由は、あなたが言ったように、SelectedDates が読み取り専用としてマークされているためです。

メンバーを読み取り専用としてマークすることは、それがすぐに割り当てられるか、クラス/構造体/などのコンストラクターでのみ割り当てられることを意味します。

ただし、そのオブジェクト内のデータを変更できないという意味ではありません。

これからは追加情報です

ELEMENTS の変更を許可しないクラス ReadOnlyCollection があります。

たとえば、読み取り専用の 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 に答える