编辑方法在 List 属性中为什么使用 get 访问器?

huangapple go评论132阅读模式
英文:

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对象上调用AddRemove,您需要首先从属性中获取该对象,因此执行的是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&lt;string&gt; MyList {get; set;} = new List&lt;string&gt;();
}

And you have an instance of MyClass:

var myInstance = new MyClass();

You then have code like this:

myInstance.MyList.Add(&quot;foo&quot;);

The question is why this uses the get accessor for the MyList property instead of the set, even though it changes the List&lt;string&gt; property.


The answer is the code starts from the myInstance object and then needs to GET the List&lt;string&gt; 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&lt;string&gt;();

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&lt;string&gt; MyList {get; private set;} = new List&lt;string&gt;();
}

or like this:

public class MyClass
{
    public List&lt;string&gt; MyList {get;} = new List&lt;string&gt;();
}

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&lt;T&gt;: IList&lt;T&gt;
	where T: class, new()
{
	private List&lt;T&gt; _list;
		
	public DatabaseList()
	{
		_list = new List&lt;T&gt;();
		// 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
}

huangapple
  • 本文由 发表于 2023年3月12日 09:48:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/75710644.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定