【问题标题】:Multiple index properties on a type?一个类型的多个索引属性?
【发布时间】:2011-06-11 10:37:07
【问题描述】:

在 C# 中有可能有这样的东西吗?我不太确定:

class Library
{
    public string Books[string title]
    {
        get{return this.GetBookByName(string title);}
    }

    public DateTime PublishingDates[string title]
    {
        get{return this.GetBookByName(string title).PublishingDate;}
    }
}

所以它可以这样使用:

myLibrary.Books["V For Vendetta"]
myLibrary.PublishingDates["V For Vendetta"] = ...

所以我需要在我的框架中实现(通过调用它们)的完整成员方法是:

GetCustomStringValue (key)
GetCustomIntValue (key)
GetCustomBoolValue (key)
GetCustomFloatValue (key)
SetCustomStringValue (key)
SetCustomIntValue (key)
SetCustomBoolValue (key)
SetCustomFloatValue (key)

我想以我自己的类型更简洁地实现它们。

【问题讨论】:

  • 这是什么意思?为什么不能只使用普通的旧常规方法来获取和设置?
  • 只是觉得有人可能会想出一个更好的解决方案。以这种方式拥有它似乎并不优雅,而只是实验。
  • 多个索引属性可用于 WPF 绑定

标签: c# .net


【解决方案1】:

只是使用多个索引器参数的风格练习:

enum Target
{
    Books,

    PublishingDates
}

class Book
{
    public string Title { get; set; }

    public DateTime PublishingDate { get; set; }
}

class Library
{
    public object this[Target target, string title]
    {
        get
        {
            switch (target)
            {
                case Target.Books:
                    return GetBookByTitle(title);

                case Target.PublishingDates:
                    return GetBookByTitle(title).PublishingDate;

                default:
                    throw new ArgumentOutOfRangeException(nameof(target), 
                        target, null);
            }
        }
    }

    static Book GetBookByTitle(string title)
    {
        return new Book {Title = "V For Vendetta"};
    }
}

var book = (Book)myLibrary[Target.Books, "V For Vendetta"];
var dateTime = (DateTime)myLibrary[Target.PublishingDates, "V For Vendetta"];

【讨论】:

    【解决方案2】:

    您可以这样做的唯一方法是让Books 成为一个属性,该属性返回具有自己合适索引器的类型。这是一种可能的方法:

    public class Indexer<TKey, TValue>
    {
        private Func<TKey, TValue> func;
    
        public Indexer(Func<TKey, TValue> func)
        {
            if (func == null)
                throw new ArgumentNullException("func");
    
            this.func = func;
        }
    
        public TValue this[TKey key]
        {
            get { return func(key); }
        }
    }
    
    class Library
    {
        public Indexer<string, Book> Books { get; private set; }
        public Indexer<string, DateTime> PublishingDates { get; private set; }
    
        public Library()
        {
            Books = new Indexer<string, Book>(GetBookByName);
            PublishingDates = new Indexer<string, DateTime>(GetPublishingDate);
        }
    
        private Book GetBookByName(string bookName)
        {
            // ...
        }
    
        private DateTime GetPublishingDate(string bookName)
        {
            return GetBookByName(bookName).PublishingDate;
        }
    }
    

    但是您应该认真考虑提供IDictionary&lt;,&gt; 的实现而不是使用这种方法,因为它会允许其他漂亮的东西,例如键值对的枚举等。

    【讨论】:

    • @Joan:没问题。确保您获得最新的副本,因为我刚刚将 Books/PublishingDates 修复为公共字段 - 现在它们是属性。
    • 谢谢,我想知道这是否也可以让我设置索引值?像 myLib.Books["name"] = new Book();等等?
    • @Joan Venge:如果您想这样做,请将类更改为也采用Action&lt;TKey, TValue&gt;,将其存储在一个字段中,然后添加一个 setter:set { action(key, value); }。或者,将类型改为接口并获取Library 以使用接口的私有实现。
    • 这个解决方案很荒谬。一个完全独立的类和一个冗余的委托调用只是为了能够使用方括号而不是圆括号的奇怪目标。用普通的方法就好了,就这样就好了……
    • @cdhowie:感谢您的解释。实际上,如果您能在示例中的代码中向我展示,我将不胜感激。基本上我的 get 和 set 方法不同而已。
    【解决方案3】:

    在 C# 中,必须将索引器称为 this(请参阅 http://msdn.microsoft.com/en-us/library/aa664459(v=VS.71).aspx)。您可以重载索引器,但请记住,C# 不允许仅基于返回类型进行重载。所以,虽然你可以:

    public int this[int i]
    public string this[string s]
    

    你不可能有:

    public int this[int i]
    public string this[int i]
    

    .NET 类库设计指南建议每个类只有一个索引器。

    所以在你的情况下,没有办法只使用索引器来做你要求的事情。

    【讨论】:

      【解决方案4】:

      不幸的是,C# 不支持它。它只识别this[] 属性,编译时它实际上只是一个名为Item 的可索引属性。不过,CLI 支持任意数量的可索引属性,这可以反映在 F# 等其他一些语言中,您可以在其中定义自己的。

      即使您确实在 CIL 或其他方式中定义了自己的,您仍然无法像您希望的那样从 C# 调用它们,您需要手动调用 get_Books(index); 以获取名为 Books 的属性。所有属性都只是这样的方法调用的语法糖。 C# 仅将名为 Item 的属性识别为可索引的。

      【讨论】:

      • 这是我为什么 VB.NET 比 C# 更好的论据之一——它支持参数化(索引)属性。 (别担心,为什么 C# 比 VB.NET 更好,我有很多理由!)
      • @AAT,您能否为 vb.net 发布一些代码示例的链接?我很好奇。
      • @Joan -- 这并不难:您只需定义一个像“Public Property PropertyName(paramName as ParamType) as PropertyType”这样的属性。您可以使用任何类型或数量的参数。
      【解决方案5】:

      为什么不只使用方法?

      class Library 
      {     
          public string Books(string title)
          {         
              return this.GetBookByName(title);
          }      
      
          public DateTime PublishingDates(string title)
          {         
              return this.GetBookByName(title).PublishingDate;   
          } 
      } 
      

      【讨论】:

      • 我可以,但是这个具体的例子我有两个 Get 和 Set 方法都像这样工作,所以我认为如果我有一个索引属性,每个都有一个索引属性,而不是两个方法.它们的返回类型不同,其余的都是一样的,你传递一个键,得到一个 int、bool、float 或 string 类型的值。
      • 我同意它不能编译,但@Kirk 的观点仍然有效。
      • 你需要摆脱gets!
      • 它可以按照现在的方式进行编译——它解决了问题的实际问题。点赞!
      • 感谢大家的编辑;我希望你明白我的意思,即使缺乏执行力。 OP 不需要让事情变得比他们需要的更复杂......
      猜你喜欢
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 2022-12-19
      • 2021-03-07
      • 2017-11-25
      • 1970-01-01
      • 2020-07-16
      • 2013-12-27
      相关资源
      最近更新 更多