C# Auto 属性 - 这是“模式”吗?最佳实践?

发布于 2024-08-12 16:22:04 字数 766 浏览 3 评论 0原文

我似乎在我的代码中经常使用这种模式,我知道它不再是一个简单的自动属性了:

  public IList<BCSFilter> BCSFilters { get; set; }

我一直在使用的代码是这样的:

    private IList<BCSFilter> _BCSFilters;

    /// <summary>
    /// Gets or sets the BCS filters.
    /// </summary>
    /// <value>The BCS filters.</value>
    public IList<BCSFilter> BCSFilters
    {
        get
        {
            if (_BCSFilters == null)
            {
                _BCSFilters = new List<BCSFilter>();
            }

            return _BCSFilters;
        }
        set
        {
            _BCSFilters = value;
        }
    }

这样我就可以做 MainClass.BCSFilters不用担心需要在使用代码中实例化 List。这是“正常”模式\正确的方法吗?

我找不到重复的问题

I seem to be using this sort of pattern in my code a lot , I know that it is not a simple Autoproperty any more as that would be:

  public IList<BCSFilter> BCSFilters { get; set; }

The code I have been using is this:

    private IList<BCSFilter> _BCSFilters;

    /// <summary>
    /// Gets or sets the BCS filters.
    /// </summary>
    /// <value>The BCS filters.</value>
    public IList<BCSFilter> BCSFilters
    {
        get
        {
            if (_BCSFilters == null)
            {
                _BCSFilters = new List<BCSFilter>();
            }

            return _BCSFilters;
        }
        set
        {
            _BCSFilters = value;
        }
    }

This is so I can just do MainClass.BCSFilters and not worry about needing to instantiate the List in the consuming code. Is this a 'normal' pattern \ the correct way to do this?

I couldn't find a duplicate question

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

断舍离 2024-08-19 16:22:04

这是我自己经常使用的一种技术。这还可以帮助节省内存资源,因为它不会实例化 List<> 。 object 除非在使用代码中实际使用了对象属性。这使用了“延迟加载”技术。

此外,您列出的“延迟加载”技术不是线程安全的。如果碰巧同时有多个对该属性的调用,您最终可能会多次调用将该属性设置为新的 List<>对象,从而用新的空 List<> 覆盖任何现有的 List 值。目的。要使 Get 访问器线程安全,您需要使用 Lock声明,如下所示:

private IList<BCSFilter> _BCSFilters;

// Create out "key" to use for locking
private object _BCSFiltersLOCK = new Object();

/// <summary>
/// Gets or sets the BCS filters.
/// </summary>
/// <value>The BCS filters.</value>
public IList<BCSFilter> BCSFilters
{
    get
    {
        if (_BCSFilters == null)
        {
            // Lock the object before modifying it, so other
            // simultaneous calls don't step on each other
            lock(_BCSFiltersLOCK)
            {
                if (_BCSFilters == null)
                }
                    _BCSFilters = new List<BCSFilter>();
                }
            }
        }

        return _BCSFilters;
    }
    set
    {
        _BCSFilters = value;
    }
}

但是,如果您始终需要 List<>对象实例化要简单一些,只需在对象构造函数中创建它并使用自动属性即可。如下所示:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}

此外,如果将“set”访问器保留为公共,则使用代码将能够将该属性设置为 Null,这可能会破坏其他使用代码。因此,防止使用代码将属性值设置为 Null 的一个好方法是将 set 访问器设置为私有。像这样:

public IList<BCSFilter> BCSFilters { get; private set; }

相关技术是返回 IEnumerable<> 。相反,来自属性的对象。这将允许您替换 List<>随时在对象内部键入,使用的代码不会受到影响。返回 IEnumerable<>你可以只返回简单的 List<>直接对象,因为它实现了 IEnumerable<>界面。就像下面这样:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IEnumerable<BCSFilter> BCSFilters { get; set; }
}

This is a technique that I use a lot myself. This can also help save memory resources since it doesn't instantiate the List<> object unless the objects property is actually being used within the consuming code. This uses a "Lazy Loading" technique.

Also, the "Lazy Loading" technique that you listed isn't Thread Safe. If there happens to be multiple calls simultaneously to the property you could end up having multiple calls setting the property to a new List<> object, consequentially overwriting any existing List values with a new, empty List<> object. To make the Get accessor Thread Safe you need to use the Lock statement, like so:

private IList<BCSFilter> _BCSFilters;

// Create out "key" to use for locking
private object _BCSFiltersLOCK = new Object();

/// <summary>
/// Gets or sets the BCS filters.
/// </summary>
/// <value>The BCS filters.</value>
public IList<BCSFilter> BCSFilters
{
    get
    {
        if (_BCSFilters == null)
        {
            // Lock the object before modifying it, so other
            // simultaneous calls don't step on each other
            lock(_BCSFiltersLOCK)
            {
                if (_BCSFilters == null)
                }
                    _BCSFilters = new List<BCSFilter>();
                }
            }
        }

        return _BCSFilters;
    }
    set
    {
        _BCSFilters = value;
    }
}

However, if you'll always need the List<> object instantiated it's a little simpler to just create it within the object constructor and use the automatic property instead. Like the following:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}

Additionally, if you leave the "set" accessor public then the consuming code will be able to set the property to Null which can break other consuming code. So, a good technique to keep the consuming code from being able to set the property value to Null is to set the set accessor to be private. Like this:

public IList<BCSFilter> BCSFilters { get; private set; }

A related technique is to return an IEnumerable<> object from the property instead. This will allow you to replace the List<> type internally within the object at any time and the consuming code will not be affected. To return IEnumerable<> you can just return the plain List<> object directly since it implements the IEnumerable<> interface. Like the following:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IEnumerable<BCSFilter> BCSFilters { get; set; }
}
仅此而已 2024-08-19 16:22:04

您的模式是完全合理的延迟加载模式,但请注意它不是线程安全的。

如果两个线程第一次非常接近地访问此属性,则 null 检查模式不会阻止一个线程评估为 null 的情况,但它有机会初始化列表之前,第二个线程也评估为 null。在这种情况下,他们都会初始化列表。

此外,当属性返回时,一个线程有可能拥有该列表的一份副本,而第二个线程则拥有另一份。

在单线程环境中这不是问题,但绝对是需要注意的事情。

Your pattern is a totally reasonable lazy-load pattern, but do be aware that it is not thread safe.

If two threads accessed this property for the first time very close together, your null check pattern would not prevent the condition where one thread evaluates to null but before it gets a chance to initialise the list, the second thread also evaluates to null. In this situation they will both initialise the list.

Furthermore, there is a chance that by the time the property returns, one thread will have one copy of the list, while the second has another.

Not an issue in a single threaded environment, but definitely something to be aware of.

坠似风落 2024-08-19 16:22:04

只要您想要的是,它就是正确的模式:

  • 允许外部代码替换整个列表(instance.BCSFilters = null)
  • 在读取时神奇地创建列表。但这很棘手,因为您允许用户将其设置为空(在集合中),但您不允许它保持为空(因为稍后的获取将产生一个空列表)。

您还可以希望以只读模式公开 IList(如果需要,可以使用惰性初始化),这样用户只能添加或删除其中的项目,而不能覆盖列表本身。如果你有很多作业,可能会涉及到抄袭。

通常我只有一个 IList 成员的 getter,我什至可以公开 IEnumerable 或在 get 中返回一个副本(但您必须提供特定的 Add 和 Remove 方法,所以 YMMV)

It's the correct pattern as long as what you want is:

  • Allow external code to replace the entire list ( instance.BCSFilters = null )
  • Have the list magically created on read. It's tricky though as is, since you're allowing the user to set it to null (in the set) but you're not allowing it to remain null (since a later get will yield an empty list).

You can also want to expose the IList in readonly mode (with lazy init if you want), so users can only Add or Remove items in it without being able to overwrite the list itself. If you have a lot of assignements, there may be copying involved though.

Usually I only have a getter to my IList members, and I may even expose IEnumerable or return a copy in the get (but you'd have to provid specific Add and Remove methods, so YMMV)

凡尘雨 2024-08-19 16:22:04

仅供参考,一种更简洁的方式来完成您已经在做的完全相同的事情可能如下所示:

private IList<BCSFilter> _BCSFilters;

public IList<BCSFilter> BCSFilters
{
    get
    {
        return _BCSFilters ?? (_BCSFilters = new List<BCSFilter>());
    }
    set
    {
        _BCSFilters = value;
    }
}

FYI, a somewhat more concise way of doing the exact same thing you're already doing might look like this:

private IList<BCSFilter> _BCSFilters;

public IList<BCSFilter> BCSFilters
{
    get
    {
        return _BCSFilters ?? (_BCSFilters = new List<BCSFilter>());
    }
    set
    {
        _BCSFilters = value;
    }
}
祁梦 2024-08-19 16:22:04

您的方法是惰性初始化版本

public class xyz
{
    public xyz()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}

Your approach is the lazy init version of

public class xyz
{
    public xyz()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}
小忆控 2024-08-19 16:22:04

还有另一个技巧:)

要使用 .Net 4 中的 Lazy。

我在 Mark Seemann 博客上看到了这一点,我认为:

 public class Order
{
    public Order()
    {
        _customerInitializer = new Lazy<Customer>(() => new Customer());
    }

    // other properties

    private Lazy<Customer> _customerInitializer;
    public Customer Customer
    {
        get
        {
            return _customerInitializer.Value;
        }
    }

    public string PrintLabel()
    {
        string result = Customer.CompanyName; // ok to access Customer
        return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value
    }
}

请注意“_customerInitializer”永远不会为 null,因此使用它是非常相同的。
而且它也可以是线程安全的!
构造函数可以使用 LazyThreadSafetyMode 枚举获得重载!
http://msdn.microsoft.com/en-us/library /system.threading.lazythreadsafetymode.aspx

there's another trick :)

To use Lazy from .Net 4.

i've seen this at Mark Seemann blog i think:

 public class Order
{
    public Order()
    {
        _customerInitializer = new Lazy<Customer>(() => new Customer());
    }

    // other properties

    private Lazy<Customer> _customerInitializer;
    public Customer Customer
    {
        get
        {
            return _customerInitializer.Value;
        }
    }

    public string PrintLabel()
    {
        string result = Customer.CompanyName; // ok to access Customer
        return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value
    }
}

notice that "_customerInitializer" could never be null so it's very same to use it.
and it could be thread safe too!
the constructor can get an overload with the LazyThreadSafetyMode Enum!
http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx

调妓 2024-08-19 16:22:04

这是延迟加载模式的示例。这是一种公认​​的模式并且完全有效。

您可以在 C# 中使用自动属性,并将实例分配给构造函数中的属性。延迟加载模式的好处是,除非调用属性,否则不会初始化该属性。这在初始化成本昂贵的情况下非常有用。

我倾向于更喜欢带有构造函数初始化的自动属性,因为语法更简洁,并且输入更少除非初始化成本很高,在这种情况下延迟加载效果很好。

This is an example of the Lazy Load pattern. It's an accepted pattern and perfectly valid.

You could use automatic properties in C# and assign an instance to the property in a constructor. The benefit of the Lazy Load pattern is that you don't initialize the property unless it's called. This can be useful in situations where the initialization is expensive.

I tend to prefer automatic properties with constructor initialization because the syntax is more concise and there's less typing unless initialization is expensive, in which case Lazy Load works well.

夏末 2024-08-19 16:22:04

是的,这很正常;-)

这种懒惰的创作并不罕见,而且很有意义。唯一需要注意的是,如果您引用该字段,则必须小心。

编辑:但我必须同意 Chris 和其他人的观点:使用 auto 属性并在构造函数中初始化集合是一​​个(好多)更好的模式。

Yeah, that's perfectly normal ;-)

Such lazy creation is not uncommon, and makes good sense. The only caveat is you'll have to be careful if you reference the field at all.

Edit: But I'll have to go with Chris and the others: it's a (much) better pattern to use an auto property and initialize the collection in the constructor.

轻拂→两袖风尘 2024-08-19 16:22:04

我们在我的工作场所使用这种模式。它很方便,因为您可以避免使用代码中可能出现的空引用异常,并使使用代码更简单。

We use that pattern at my workplace. It's handy because you avoid possible null reference exceptions in the consuming code, and keeps the consuming code simpler.

爱格式化 2024-08-19 16:22:04

这是一个不错的模式。 Autoproperties 只是最简单、可以说是最常见的属性场景的简写,对于某些场景,使用它根本不明智。不过,您可以做的是在构造函数中实例化 BCSFilters。这样您就可以使用自动属性,并且仍然不必担心空引用异常。

It's an alright pattern. Autoproperties is just shorthand for the very most simple, and arguably most common scenario with properties, and for some scenarios, it simply isn't sensible to use it. What you could do, tho, is to instantiate BCSFilters in the constructor instead. That way you could use autoproperties and you still wouldn't have to worry about null reference exceptions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文