Inferring 2 out of 3 generic types
Asked Answered
S

1

7

I've built a method that returns me an instance of attribute if it is found on the property:

public static U GetPropertyAttribute<T, TProperty, U>(this T instance, Expression<Func<T, TProperty>> propertySelector, U attribute) where U : Attribute
{
   return Attribute.GetCustomAttribute(instance.GetType().GetProperty((propertySelector.Body as MemberExpression).Member.Name), typeof(U), true) as U;
}

In order to get instance, I have to invoke:

var cc = new CustomClass();
cc.GetPropertyAttribute(x => x.Name, new NullableAttribute())

And it works fine, I get exact instance of the attribute class.

However, I don't like that I have to use new NullableAttribute() as parameter, I'd like to have invoke look like:

cc.GetPropertyAttribute<NullableAttribute>(x => x.Name)

This however does not work due to the fact that as soon as I remove second parameter and add 1 generic type in method name, it starts to require other two as well. Is there a way to force the method to infer 2 out of 3 generic parameters? I.e. I want to specify attribute class, but don't want to specify class name and property name.

UPDATE:

Here's the implemented code, thanks to Jon, as well as the code for string solution. I've decided to nest the class so that I don't polute the namespace if I introduce same approach for some other extension classes.

public static class AttributeExtensions
{
    public static ObjectProperty<T, TProperty> From<T, TProperty>(this T instance, Expression<Func<T, TProperty>> propertySelector)
    {
        return new ObjectProperty<T, TProperty>(instance, propertySelector);
    }

    public class ObjectProperty<T, TProperty>
    {
        private readonly T instance;
        private readonly Expression<Func<T, TProperty>> propertySelector;

        public ObjectProperty(T instance, Expression<Func<T, TProperty>> propertySelector)
        {
            this.instance = instance;
            this.propertySelector = propertySelector;
        }

        public U GetAttribute<U>() where U : Attribute
        {
            return Attribute.GetCustomAttribute(instance.GetType().GetProperty((propertySelector.Body as MemberExpression).Member.Name), typeof(U), true) as U;
        }
    }

    public static T GetPropertyAttribute<T>(this object instance, string propertyName) where T : Attribute
    {
        return Attribute.GetCustomAttribute(instance.GetType().GetProperty(propertyName), typeof(T), true) as T;
    }
}

So invoke now goes like this:

var cc = new CustomClass();
var attr = cc.From(x => x.Name).GetAttribute<NullableAttribute>();
Sachiko answered 25/10, 2012 at 16:39 Comment(0)
S
8

Is there a way to force the method to infer 2 out of 3 generic parameters?

One common approach is to have an intermediate type with those two type parameters, and then have a generic method within that type to allow you to supply the last one:

public static AttributeFetcher<T, TProperty> FetchFrom<T, TProperty>
    (this T instance, Expression<Func<T, TProperty>> propertySelector)
{
    return new AttributeFetcher<T, TProperty>(instance, propertySelector);     
}

public class AttributeFetcher<T, TProperty>
{
    private readonly T instance;
    private readonly Expression<Func<T, TProperty>> propertySelector;

    // Add obvious constructor

    public U Attribute<U>() where U : Attribute
    {
        // Code as before
    }
}

Then you can write:

cc.FetchFrom(x => x.Name).Attribute<NullableAttribte>();

It's possibly that you can actually make AttributeFetcher non-generic, given that you really only need the PropertyInfo as far as I can tell. The code above is for the more general case.

Spank answered 25/10, 2012 at 16:48 Comment(4)
In my case, instead of introducing this complexity, usually I've an extension method for MemberInfo, so I can handle the same thing just calling something like obj.GetType().GetProperty("MyProperty").GetAttribute<MyAttribute>(). I understand that OP is using an expression because its advantages in terms of refactoring, but...Pantaloon
@MatíasFidemraizer Why not build a extension method which avoids calling GetType().GetProperty("..") if you have one anyway? I like the idea of Jon Skeet.Pemphigus
@Jon: This is really nice approach but is more a workaround rather than real answer. I like it though and will use it as solution if no better proposal is given! :) ThanksRating
@FelixK. You avoid that, but you call other things :D I'm not saying Skeet's solution is wrong. I just wanted to share other approach.Pantaloon

© 2022 - 2024 — McMap. All rights reserved.