英文:
Why does an editing method in a List property use the get accessor?
问题
我有一个类型为 List<T>
的属性,每当它被更新时,我想要更新数据库。
起初,我以为我可以只是在属性的 set 访问器中更新数据库,但在经过一些测试之后,我意识到像 Add()
和 Remove()
这样的方法只使用 get 访问器,而不使用 set 访问器。
为什么会这样,我该如何解决这个问题?
包含这个属性的类实现了一个需要 List
的接口。虽然我可以在接口中将属性的类型更改为 BindingList
,但并不是所有实现成员都需要知道属性何时被更新。
英文:
I have a property of type List<T>
, and I want to update a database whenever it's updated.
At first, I thought I could just update the database in the property's set accessor, but after some testing, I realized that methods like Add()
and Remove()
only use the get accessor, not the set one.
Why is this, and what can I do to work around it?
The class containing this property implements an interface which requires a List
. Although I can change the property's type to BindingList
in the interface, not all implementing members need to know when the property is updated.
答案1
得分: 1
你的属性本来就不应该有setter。该属性与List
对象本身有关,而不是List
中的项目。Setter将用于用全新的List
对象替换现有的List
对象。要在现有的List
对象上调用Add
或Remove
,您需要首先从属性中获取该对象,因此执行的是getter。
如果您需要知道现有集合对象何时更改,只有集合本身能够做到。这意味着使用像你建议的BindingList<T>
或ObservableCollection<T>
。属性本身应该是只读的,例如:
public ObservableCollection<object> Objects { get; } = new ObservableCollection<object>();
以及:
Objects.CollectionChanged += Objects_CollectionChanged;
英文:
Your property shouldn't have a setter to begin with. That property relates to the List
object itself, not the items in the List
. A setter would be used to replace the existing List
object with a completely new List
object. In order to call Add
or Remove
on the existing List
object, you need to get that object from the property first, hence the getter is what is executed.
If you need to know when the existing collection object changes, only the collection itself can do that. That means using a BindingList<T>
as you suggest, or an ObservableCollection<T>
. The property itself should be read-only, e.g.
public ObservableCollection<object> Objects { get; } = new ObservableCollection<object>();
private void Objects_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// ...
}
and:
Objects.CollectionChanged += Objects_CollectionChanged;
答案2
得分: 1
我将重申问题并使用代码片段进行翻译,以便我们有实际的标识符名称可以使用。
您有一个类,类似于这样:
public class MyClass
{
public List<string> MyList { get; set; } = new List<string>();
}
然后您有一个MyClass
的实例:
var myInstance = new MyClass();
接下来,您有这样的代码:
myInstance.MyList.Add("foo");
问题是,为什么这会使用MyList
属性的get
访问器,而不是set
,尽管它更改了List<string>
属性。
答案是,代码从myInstance
对象开始,然后需要获取MyList
属性引用的List<string>
对象,以便调用该列表的Add()
方法。就属性访问而言,无论Add()
方法如何更改列表,都不重要。在属性访问方面,您仍然是获取引用,没有其他操作。
那么,set
访问器有什么用呢?嗯...设置引用。所以现在假设您执行以下操作:
myInstance.MyList = new List<string>();
这使用set
访问器,因为它将一个新值设置为实际属性,而不仅仅是属性或属性的成员。现在,既然您了解了set
访问器实际上何时被调用,希望这有助于澄清为什么在其他情况下使用get
访问器:您没有分配新值给属性。
最后,这应该引发是否您是否真的需要属性的set
访问器的问题。
对于许多类来说,完全更改类使用的List可能会破坏与类的其他相关方面的假设。也就是说,您可能更好地声明属性如下:
public class MyClass
{
public List<string> MyList { get; private set; } = new List<string>();
}
或者像这样:
public class MyClass
{
public List<string> MyList { get; } = new List<string>();
}
因为再次强调,您想要对该特定列表实例执行的任何操作都是get
操作,而不是set
,因此set
访问通常不是必要的,甚至没有帮助。
英文:
I'll start by restating the question using code snippets so we have actual identifier names to work with.
You have a class like this:
public class MyClass
{
public List<string> MyList {get; set;} = new List<string>();
}
And you have an instance of MyClass
:
var myInstance = new MyClass();
You then have code like this:
myInstance.MyList.Add("foo");
The question is why this uses the get
accessor for the MyList
property instead of the set
, even though it changes the List<string>
property.
The answer is the code starts from the myInstance
object and then needs to GET the List<string>
object referred to by the MyList
property so it can call that list's Add()
method. It doesn't matter that the Add()
method changes the list. As far as the property access is concerned, you are still getting the reference, and nothing else.
So what is the set
accessor for, then? Well... setting the reference. So now let's say you do this:
myInstance.MyList = new List<string>();
This uses the set
accessor, because it sets a new value to the actual property and not just a property or member of the property. And now that you understand when the set
accessor is actually called, hopefully that helps clarify why the get
accessor is used in other situations: you did not assign a new value to the property.
Finally, this should raise the question of whether you want a set
accessor for the property at all.
For many classes, completely changing a List used by the class is likely to break assumptions for other related aspects of the class. That is, you're probably better off declaring the property like this:
public class MyClass
{
public List<string> MyList {get; private set;} = new List<string>();
}
or like this:
public class MyClass
{
public List<string> MyList {get;} = new List<string>();
}
Because, again, anything you want to do with that specific list instance is a get
operation, rather than a set
, and so the set
access is not really needed or even helpful.
答案3
得分: 0
以下是代码部分的翻译:
It's probably easier to create and unit test the 4 CRUD methods to work on the list as you often have to do. But, if you really want a workaround you could use IList:
public class DatabaseList<T>: IList<T>
where T: class, new()
{
private List<T> _list;
public DatabaseList()
{
_list = new List<T>();
// populate from current database content?
}
public new void Add(T item)
{
_list.Add(item);
// and do database Insert
}
// and the other 12 members of IList...
// which makes the 4 CRUD methods sounds like a lot less testing
}
英文:
It's probably easier to create and unit test the 4 CRUD methods to work on the list as you often have to do. But, if you really want a workaround you could use IList:
public class DatabaseList<T>: IList<T>
where T: class, new()
{
private List<T> _list;
public DatabaseList()
{
_list = new List<T>();
// populate from current database content?
}
public new void Add(T item)
{
_list.Add(item);
// and do database Insert
}
// and the other 12 members of IList...
// which makes the 4 CRUD methods sounds like a lot less testing
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论