0

I have a class in my program called SortableObservableCollection<T> that inherits from ObservableCollection<T>. It has a function called Sort:

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

I want to declare a property called Key1 of type Func<T, TKey>. I have tried:

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

But I am getting a syntax error on the left curly brace. The error indicates that the compiler is expecting a left parentheses. I've tried making it a field declaration:

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

But then the compiler gives me the same message on the semi-colon.

What's the right syntax for declaring this property?

Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
Tony Vitabile
  • 8,298
  • 15
  • 67
  • 123
  • 2
    A property can't be generic like a method can, see for example [Why does C# not allow generic properties?](http://stackoverflow.com/questions/8620883/). If the property is inside a class (or struct or interface) that is generic, the type of the property may of course depend on the type parameters of the containing class, but the property cannot introduce new type parameters like a generic method does. – Jeppe Stig Nielsen Feb 02 '13 at 18:39
  • @JeppeStigNielsen: Thanks. Though I didn't read all of it, that link you provided explains why I can't do what I'm trying to do. – Tony Vitabile Feb 04 '13 at 13:38

5 Answers5

3

First, you'll need to expand your class definition to include the generic parameter for TKey rather than just having it at the Sort() method level:

public class SortableObservableCollection<T, TKey>
{
}

After that, you won't need the second set of generic parameters:

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

You'll also be able to remove the generic parameter from the Sort() method.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • When I try that, I get "The type or namespace name 'TKey' could not be found (are you missing a using directive or an assembly reference?)" – Tony Vitabile Feb 02 '13 at 18:00
  • @Tony - That declaration assumes that TKey is a generic parameter to the class. If it's not, you'll need to define some concrete type rather than a generic parameter. – Justin Niessner Feb 02 '13 at 18:01
3

You're trying to create a generic property, but properties can't be generic. You have to work around that somehow. I can think of few options:

  1. Declare the delegate as object-returning:

    public Func<T, object> Key1 { get; set; }
    
  2. Make the property into a method (or two methods, if you also need a getter). The problem with this is that you would need to store the delegate in a non-generic field and it would be hard to work with that afterwards:

    private Delegate key1;
    
    public void SetKey1<TKey>(Func<T, TKey> key1)
    {
        this.key1 = key1;
    }
    
  3. If the type of the sort key won't change, make it a type parameter of your class:

    public class SortableObservableCollection<T, TKey>
    {
        public Func<T, TKey> Key1 { get; set; }
    
        …
    }
    
svick
  • 236,525
  • 50
  • 385
  • 514
1

You have 2 options here. First you can add TKey as a generic parameter to the class.

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

Now your second option if you can't or don't want to add another generic parameter to the class is to avoid using a property for Key1 all together.

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;
    }

}
blaise
  • 307
  • 1
  • 5
0

Try this:

public class SortableObservableCollection<T, TKey>:ObservableCollection<T>
{
    public Func<T,TKey> Key1 {get; set;}
}
Hossein Narimani Rad
  • 31,361
  • 18
  • 86
  • 116
0

While everyone's answers will work, they weren't exactly where I was trying to get with this. Originally, I had a method of the class called Sort:

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

This worked well, but then I found a bug in my UI that was related to calling the Sort method more than once. The sort itself worked, that wasn't the problem. The bug is convoluted and difficult to explain, and I'd rather not go into it. But the easiest solution was to make sure that the collection was sorted only once, on the correct key. The thing is that the keys differed depending on the particular drop down that the collection was bound to. So I needed a way to encapsulate the sort criteria in the collection so it would do the proper sort the first and only time.

As I already had the Sort method mentioned above, I figured the easiest thing to do was to put the keySelector parameter to it into a property of the class. It turns out that the language doesn't let you do that, and now I know and understand why.

What I ended up doing to get this to work was replace the Func property with a reference to an object that implements IComparer<T>. I have no trouble declaring this property and it's easy to declare a class that implements it for each type passed as the T parameter in a collection declaration. I just initialize the property after allocating the collection object and I rewrote the Sort method so it looks like this:

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

RowsToSkip is an int property of the class which specifies how many rows at the start of the collection are to be skipped. This lets me add an entry like "Pick a choice" at the start of the list and not have the sort move it.

Tony Vitabile
  • 8,298
  • 15
  • 67
  • 123