【问题标题】:What is the proper way to declare a property that is a function delegate声明作为函数委托的属性的正确方法是什么
【发布时间】:2013-02-02 17:57:06
【问题描述】:

我的程序中有一个名为SortableObservableCollection<T> 的类,它继承自ObservableCollection<T>。它有一个名为Sort的函数:

public void Sort<TKey>( Func<T, TKey> keySelector, int skip = 0 ) 
{
   // . . .
}

我想声明一个名为Key1 的属性,类型为Func&lt;T, TKey&gt;。我试过了:

public Func<T, TKey> Key1<T, TKey> { get; set; }

但我在左大括号上遇到语法错误。该错误表明编译器需要一个左括号。我尝试将其设为字段声明:

public Func<T, TKey> Key1<T, TKey>;

但是编译器在分号上给了我同样的信息。

声明此属性的正确语法是什么?

【问题讨论】:

  • 属性不能像方法那样是通用的,例如Why does C# not allow generic properties?。如果属性位于泛型类(或结构或接口)中,则属性的类型当然可能取决于包含类的类型参数,但属性不能像泛型方法那样引入新的类型参数。
  • @JeppeStigNielsen:谢谢。虽然我没有阅读全部内容,但您提供的链接解释了为什么我不能做我想做的事。

标签: c#


【解决方案1】:

首先,您需要扩展类定义以包含 TKey 的泛型参数,而不仅仅是在 Sort() 方法级别:

public class SortableObservableCollection<T, TKey>
{
}

之后,您将不再需要第二组泛型参数:

public Func<T, TKey> Key1 { get; set; }

您还可以从Sort() 方法中删除泛型参数。

【讨论】:

  • 当我尝试这样做时,我得到“找不到类型或命名空间名称'TKey'(您是否缺少 using 指令或程序集引用?)”
  • @Tony - 该声明假定 TKey 是类的通用参数。如果不是,则需要定义一些具体类型而不是泛型参数。
【解决方案2】:

您正在尝试创建通用属性,但属性不能是通用的。你必须以某种方式解决这个问题。我能想到几个选项:

  1. 将代理声明为object-returning:

    public Func<T, object> Key1 { get; set; }
    
  2. 将属性变成一个方法(或两个方法,如果您还需要一个 getter)。这样做的问题是您需要将委托存储在非泛型字段中,之后很难使用它:

    private Delegate key1;
    
    public void SetKey1<TKey>(Func<T, TKey> key1)
    {
        this.key1 = key1;
    }
    
  3. 如果排序键的类型不会改变,请将其作为类的类型参数:

    public class SortableObservableCollection<T, TKey>
    {
        public Func<T, TKey> Key1 { get; set; }
    
        …
    }
    

【讨论】:

    【解决方案3】:

    这里有 2 个选项。 首先,您可以将 TKey 作为泛型参数添加到类中。

    public class SortableObservableCollection<T, TKey> : ObservableCollection<T>
    {
        public void Sort(Func<T, TKey> keySelector, int skip = 0)
        {
            //...
        }
        public Func<T, TKey> Key1 { get; set; }
    }
    

    现在,如果您不能或不想向类添加另一个通用参数,那么您的第二个选择是避免一起使用 Key1 的属性。

    public class Sort2<T> : ObservableCollection<T>
    {
        Type keyType;
        object key1;
    
        public void Sort<TKey>(Func<T, TKey> selector, int skip = 0)
        {
            //...
        }
    
        public void SetKey<TKey>(TKey val)
        {
            keyType = typeof(TKey);
            key1 = val;
        }
        public TKey GetKey<TKey>()
        {
            return (TKey)key1;
        }
    
        public Type GetKeyType()
        {
            return keyType;
        }
    
    }
    

    【讨论】:

      【解决方案4】:

      试试这个:

      public class SortableObservableCollection<T, TKey>:ObservableCollection<T>
      {
          public Func<T,TKey> Key1 {get; set;}
      }
      

      【讨论】:

        【解决方案5】:

        虽然每个人的答案都会奏效,但它们并不完全是我试图解决的问题。原来,我有一个类的方法叫Sort:

        public void Sort<TKey>( Func<T, TKey> keySelector, . . . ) {
            // . . .
        }
        

        这很好用,但后来我在 UI 中发现了一个与多次调用 Sort 方法有关的错误。排序本身有效,这不是问题。该错误令人费解且难以解释,我宁愿不深入探讨。但最简单的解决方案是确保集合仅在正确的键上排序一次。问题是键会根据集合绑定到的特定下拉列表而有所不同。所以我需要一种方法来封装集合中的排序标准,这样它就可以第一次也是唯一一次进行正确的排序。

        因为我已经有了上面提到的Sort 方法,所以我认为最简单的方法是将它的 keySelector 参数放入类的属性中。事实证明,语言不允许你这样做,现在我知道并理解了原因。

        我最终要做的是将Func 属性替换为对实现IComparer&lt;T&gt; 的对象的引用。我可以轻松声明此属性,并且很容易为集合声明中作为T 参数传递的每种类型声明一个实现它的类。我只是在分配集合对象后初始化属性,并重写了Sort 方法,所以它看起来像这样:

        public void Sort() {
            if ( Comparer != null ) {
                InternalSort( Items.Skip( RowsToSkip ).OrderBy( item => item, Comparer ) );
            } else { 
                InternalSort( Items.Skip( RowsToSkip ).OrderBy( item => item ) );
            }
        }
        

        RowsToSkip 是类的int 属性,它指定要跳过集合开头的行数。这让我可以在列表的开头添加一个类似“选择一个选项”的条目,而不是让排序移动它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多