3
\$\begingroup\$

I have the following code (I need the LambdaExpression as not all Func<>'s are T, int):

var orderDelegates = new Dictionary<string, LambdaExpression>();

Expression<Func<Image, int>> id = i => i.Id;
orderDelegates.Add(ContentItem.ORDER_BY_ID, id);

Is it possible to make this shorter?

Best something like:

var orderDelegates = new Dictionary<string, LambdaExpression>()
{
    { ContentItem.ORDER_BY_ID, ????? },
    ...
};

I already tried the following, which did not work:

orderDelegates.Add(ContentItem.ORDER_BY_ID, (Expression<Func<Image, int>>)i => i.id);
//or
orderDelegates.Add(ContentItem.ORDER_BY_ID, i => i.id as Expression<Func<Image, int>>);

I "improved" it a little to the following already, but "full inlining" would be the desired result:

public static Dictionary<string, LambdaExpression> GetOrderDelegates<T>() where T : ContentItem
{
    Expression<Func<T, int>> id = i => i.Id;
    Expression<Func<T, bool>> enabled = i => i.IsEnabled;
    Expression<Func<T, IComparable>> author = i => i.Creator.UserName;
    //...

    return new Dictionary<string, LambdaExpression>()
        {
            { ContentItem.ORDER_BY_ID, id },
            { ContentItem.ORDER_BY_ENABLED, enabled },
            { ContentItem.ORDER_BY_AUTHOR, author },
            //...
        };
}
\$\endgroup\$
4
  • \$\begingroup\$ So, what do you know about the lambdas, are they all Func<Image, something> or at least Func<something1, something2>? \$\endgroup\$
    – svick
    Aug 17, 2013 at 11:16
  • \$\begingroup\$ @svick: Yes, they are all Func<T, something>... \$\endgroup\$ Aug 17, 2013 at 11:45
  • \$\begingroup\$ whats ContentItem? \$\endgroup\$
    – Nikita B
    Aug 17, 2013 at 12:38
  • \$\begingroup\$ @Nik: ContentItem is my base class, which holds all common properties like Id and all my (content-)items use classes derived from it ... \$\endgroup\$ Aug 17, 2013 at 17:25

1 Answer 1

1
\$\begingroup\$

You can do this by creating a custom class that supports collection initializers. Its Add() method would be generic, so that it fits any allowed expression:

class ExpressionDictionary<T> : IEnumerable<KeyValuePair<string, LambdaExpression>>
{
    private readonly Dictionary<string, LambdaExpression> m_dictionary
        = new Dictionary<string, LambdaExpression>();

    public void Add<TResult>(string key, Expression<Func<T, TResult>> expression)
    {
        m_dictionary.Add(key, expression);
    }

    public IEnumerator<KeyValuePair<string, LambdaExpression>> GetEnumerator()
    {
        return m_dictionary.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Usage:

new ExpressionDictionary<T>
{
    { ContentItem.ORDER_BY_ID, i => i.Id },
    { ContentItem.ORDER_BY_ENABLED, i => i.IsEnabled },
    //the cast is necessary if the result has to be Func<T, IComparable>
    // and not Func<T, string>
    { ContentItem.ORDER_BY_AUTHOR, i => (IComparable)i.Creator.UserName },
    //...
};

You will need to add other methods to ExpressionDictionary. Either implement the whole IDictionary<TKey, TValue> interface and make GetOrderDelegates() return that. Or just add a getter for the private Dictionary and return that.

But even better option might be not to use Dictionary at all. Instead create OrderByDeletegates class (though that's probably not the best name, since it would contain expressions, not delegates) that would have properties for all expressions:

class OrderByDeletegates<T> where T : ContentItem
{
    public Expression<Func<T, int>> OrderById { get; set; }
    public Expression<Func<T, bool>> OrderByEnabled { get; set; }
    public Expression<Func<T, IComparable>> OrderByAuthor { get; set; }
    // …
}

Usage:

new OrderByDeletegates<T>
{
    OrderById = i => i.Id,
    OrderByEnabled = i => i.IsEnabled,
    OrderByAuthor = i => i.Creator.UserName,
    // …
};

The advantage of this is that it's type-safe. You can't for example accidentally do something like OrderByEnabled = i => i.Id.

\$\endgroup\$
5
  • \$\begingroup\$ This are great ideas, but the second one will not work in my case, as I use the string-key of the dictionary to select the correct delegate/expression... I try implementing your first idea as an extension to my dictionary and report back. \$\endgroup\$ Aug 17, 2013 at 17:30
  • \$\begingroup\$ @chrfin Why do you need to use the key? Can't you just access the properties directly? \$\endgroup\$
    – svick
    Aug 17, 2013 at 18:19
  • \$\begingroup\$ Because the key comes in as a string from the (Web-)Front-End (e.g "mydomain.com/page?sortKey=Author" will sort after the expression with the key "Author")... \$\endgroup\$ Aug 17, 2013 at 19:20
  • \$\begingroup\$ Went with your first solution and works fine... \$\endgroup\$ Aug 18, 2013 at 9:16
  • \$\begingroup\$ note that Expression<Func<T, TResult>> does not guarantee that TResult is a member of T which I think was the op intent \$\endgroup\$
    – kofifus
    Jun 17, 2021 at 0:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.