Extension method that accepts Expression<Func<T>> expression as parameter
Asked Answered
P

3

12

I am using .NET4.5 and C# I fancied creating extension method that would allow me to pass property of object and if Id of that object is 0 then return null otherwise return that property value.

I could do it no problem with reflection so consider this more of training exercise and not me trying to solve the actual problem.

Currently extension method is sitting in static class looking like this:

    public static object GetNullIfNotSet(this WillAnswer answer, Expression<Func<WillAnswer>> expression)
    {
        if (answer.Id == 0) return null;
        return expression.Compile()();
    }

The way I want to be able to use it is following (answer is of type WillAnswer):

var emptyIfNewObject = answer.GetNullIfNotSet(o => o.HasBusinessAssets)

However it gives me compilation error:

Error 1 Delegate 'System.Func' does not take 1 arguments C:\hg\Website\Areas\Wills\ViewModel\Answers.cs 38 59 Website enter image description here

Which makes me frown since I don't think I am passing any arguments (am I?). Could please someone smarter than myself explain which of my expectations is wrong.

Just in case I wasn't clear I will reiterate. What I want is to be able to call var emptyIfNewObject = answer.GetNullIfNotSet(o => o.HasBusinessAssets) and get null if Id of answer is 0.

Peridot answered 14/10, 2015 at 9:5 Comment(3)
As others have pointed out, you don't need an Expression. The source of your error is the fact, that you defined your func wrong, though. You probably want something like Func<object, WillAnswer> (with object being the return type).Jahdal
call it in this way: answer.GetNullIfNotSet(() => (((Func<WillAnswer>)(() => answer.HasBusinessAssets))())); what a mess! go back to haim770's answer :-)Retentive
If you have access to the object of which you are accessing the property when you call the method, which you do as it's an extension on the object itself, then why not just pass the property instead of a Func?Peder
B
14

No need for Expression at all, just use Func<WillAnswer, TProp>:

public static TProp GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func)
{
    if (answer.Id == 0) return default(TProp);
    return func(answer);
}

Please note, that this will not always return null but the default value (in case the property is a value-type).

Update (as per your request):

In order to be able to return null for all passed properties, the method signature was changed to return object instead:

public static object GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func)
{
    if (answer.Id == 0) return null;
    return func(answer);
}

But, you'll lose the benefits of generics and you'll end up with explicit casts to Nullable<T>:

var emptyIfNewObject = (bool?)answer.GetNullIfNotSet(o => o.HasBusinessAssets)

Which is less ideal.

Bartizan answered 14/10, 2015 at 9:11 Comment(8)
Great answer but, how do I get it to return null?Peridot
In order to always return null, the compiler will have to make sure the passed property x => x.SomeProp is a reference type. This can be done by adding a generic constraint, but it means that you won't be able to return x => x.SomeInt (for example). Is that what you need?Bartizan
Could you do slight aleteration in you answer to instead of TProp return object this allows to return null public static object GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func) { if (answer.Id == 0) return null; return func(answer); }Peridot
Thank you very much. I appreciate second solution is not as elegant, however some users will find it useful. Thanks again.Peridot
@MatasVaitkevicius - I usually write two methods in these cases - one for reference types, TProp GetNullIfNotSet<TProp>(…) where TProp:class, and another for value types TProp? GetNullableIfNotSet<TProp>(…) where TProp:struct.Aspirant
@חיים - wouldn't the second method fail when you try to cast (bool)null?Aspirant
@Aspirant It would, but one should cast to (bool?)Peridot
@Kobi, Actually, I didn't try it yet. Perhaps your suggestion can solve the problem (I'll add it to answer if you permit - or write your own answer).Bartizan
E
7

It seems that you need a Func<WillAnswer, T> not an expression:

  public static T GetDefaultIfNotSet<T>(this WillAnswer answer, Func<WillAnswer, T> func) {
    if (null == answer)   
      throw new ArgumentNullException("answer");
    else if (null == func)   
      throw new ArgumentNullException("func");

    return answer.Id == 0 ? return default(T) : func(answer);
  }

EDIT: if you want to ensure null you can restrict generic T:

     public static T GetNullIfNotSet<T>(this WillAnswer answer, Func<WillAnswer, T> func) 
       where T: class { // no structs here
         if (null == answer)   
           throw new ArgumentNullException("answer");
         else if (null == func)   
           throw new ArgumentNullException("func");

         return answer.Id == 0 ? return null : func(answer);
     } 
Elwandaelwee answered 14/10, 2015 at 9:16 Comment(9)
I would also suggest to rename method name to something like GetDefaultIfNotSet, because the semantics has changed — it is not possible to return null value for bool property.Wellappointed
@Sergey Kolodiy: thank you! I've edited the answer, GetDefaultIfNotSet is a far better name.Elwandaelwee
@SergeyKolodiy but I need that null, this is the essence of the questionPeridot
@Matas Vaitkevicius: null is a default for classes; e.g.default(typeof(Object)) is null while default(typeof(int)) is 0Elwandaelwee
@MatasVaitkevicius then you can return an object instead of T (but you will loose the type!). Or use the generic constraint.Wellappointed
@SergeyKolodiy I need to get null for bools in this specific case and potentially other in futurePeridot
@Matas Vaitkevicius: if you want true, false, null (i.e. nullabe boolean) have a look at Boolean?; default(typeof(Boolean?)) is nullElwandaelwee
@SergeyKolodiy it did it ` public static object GetNullIfNotSet<TProp>(this WillAnswer answer, Func<WillAnswer, TProp> func) { if (answer.Id == 0) return null; return func(answer); }` thank you.Peridot
@MatasVaitkevicius, as @Dmitry pointed out, using bool? is a better choice: use generic version with bool? generic parameter: var result = answer.GetDefaultIfNotSet<bool?>(o => o.HasBusinessAssets);.Wellappointed
B
0

Yes you can:

public static void LoadProperty<T>(this T t, Func<T, object> func) where T: Entity
{       

}

and you can use :

contact.LoadProperty(p => p.LogicalName);
Beware answered 26/12, 2020 at 14:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.