【问题标题】:C# Indexer Property QuestionC# 索引器属性问题
【发布时间】:2010-11-08 11:05:45
【问题描述】:

我有这样的课:

public class SomeClass
{
    private const string sessionKey = "__Privileges";
    public Dictionary<int, Privilege> Privileges
    {
        get
        {
            if (Session[sessionKey] == null)
            {
                Session[sessionKey] = new Dictionary<int, Privilege>();
            }

            return (Dictionary<int, Privilege>)Session[sessionKey];
        }
    }
}

现在,如果我这样做......

var someClass = new SomeClass();
var p = someClass.Privileges[13];

...而且没有key 13,我会得到这样的错误:

The given key was not present in the dictionary.

我希望有一个可以通过与上面相同的方式访问的属性,但在缺少键的情况下会返回一个默认对象。

我尝试创建这样的索引器属性...

    public Privilege Privileges[int key]
    {
        get
        {
            try { return _privileges[key]; }
            catch { return new Privilege(); }
        }
    }

...但看起来这不是 C# 2008 语言功能。

如何以相同的方式访问属性,但如果键不存在则获取默认对象?

【问题讨论】:

    标签: c# .net dictionary properties indexer


    【解决方案1】:

    如您所见,C# 不支持命名索引器。

    您是否考虑过使用常规方法而不是索引器属性?并非每个编程问题都需要使用花哨的语法来解决。是的,您可以使用聚合字典创建自己的 IDictionary 实现并更改属性访问行为 - 但对于仅获取值或返回默认值的东西真的有必要吗?

    我会在你的类中添加这样的方法:

    protected Privilege GetPrivilege(int key)
    {
        try { return _privileges[key]; }
        catch { return new Privilege(); }
    }
    

    或者更好的是,避免将异常处理作为流控制机制:

    protected Privilege GetPrivilge( int key )
    {
        Privilege priv;
        if( _privileges.TryGetValue( key, out priv ) )
            return priv;
        else
            return new Privilege();
    }
    

    【讨论】:

    • 好建议。我采纳了你的建议来解决我的问题。我之前使用过异常处理来进行流控制,但直到现在才认为它是一件坏事。谢谢。
    【解决方案2】:

    您应该使用以下语法来检索值:

    public Privilege this[int key]
    {
        get
        {
            var value = (Privilege)null;
            if(!_privileges.TryGetValue(key, out value))
                value = new Privilege();
            return value;
        }
    }
    

    我对IDictionary的这种使用需求很大,所以做了一些扩展方法:

    public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key)
    {
        TValue v = default(TValue);
        d.TryGetValue(key, out v);
        return v;
    }
    public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, Func<TValue> value)
    {
        TValue v = d.Get(key);
        if (v == null)
        {
            v = value();
            d.Add(key, v);
        }
        return v;
    }
    

    现在你可以写了:

    public Privilege this[int key]
    {
        get
        {
            return _privileges.Get(key, () => new Privilege());
        }
    }
    

    【讨论】:

    • 那不是有效的 C#!索引器只能与 this 关键字一起使用。
    【解决方案3】:

    C# 中的Indexers 只能与this 关键字一起使用。

    我怀疑你想要这样的东西:

    public Privilege this[int key]
    {
        get
        {
            try { return _privileges[key]; }
            catch { return default(Privelege); }
        }
    }
    

    您可以直接在SomeClass 中定义,这样您就可以访问以下特权项目:

    SomeClass foo;
    var bar = foo[100];
    

    或在从IDictionary&lt;TKey, TValue&gt; 实现的自定义类中定义此索引器(并在内部包含一个Dictionary&lt;TKey, TValue,用于实际存储数据)。然后你可以像这样使用它:

    SomeClass foo;
    var bar = foo.Priveleges[100];
    

    您似乎建议使用哪种语法,并且可能最合适,尽管这需要更多的努力。

    【讨论】:

    • 使用异常处理作为控制流是个坏主意。它使您的程序难以调试(因为通常您希望在第一次机会异常跟踪的情况下进行调试,并且许多无关的异常使调试变得更加困难),它使它们变慢,并且它违背了异常应该被设计的事实例外。最好先检查密钥是否存在,而不是尝试它并在不存在时捕获异常。
    • @Eric:非常公平 - 在发布回复之前我就知道了;但是,该问题还与问题的属性/索引器方面有关。您可能应该对原始问题发表评论。
    【解决方案4】:

    您必须使用具有所需行为的索引器定义自己的基于 IDictionary 的类,并在您的属性 getter 中返回其实例,而不是常用的 Dictionary 类。

    【讨论】:

      猜你喜欢
      • 2016-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-22
      • 1970-01-01
      • 2011-11-11
      • 2011-12-10
      相关资源
      最近更新 更多