1

Please consider the following code. I am surprised to learn that output is double "Base" rather than "Base" followed by "Derived".

Is there something that I am doing wrong? Can this be written differently to get the custom attribute based on an expression. It appears as if expressions will always use the base class.

The commented line proves that the custom attribute is accessible via reflection.

public static class Program
{
    private static void Main(string[] args)
    {
        Write((Derived data) => data.Code);
        Write((Base data) => data.Code);

        // Console.WriteLine(typeof(Derived).GetProperty(nameof(Derived.Code)).GetCustomAttributes<XmlElementAttribute>().First().ElementName);
    }

    private static void Write<T1,T2>(Expression<Func<T1,T2>> expression)
    {
        Console.WriteLine(((MemberExpression) expression.Body).Member.GetCustomAttribute<XmlElementAttribute>().ElementName);
    }
}

public abstract class Base
{
    [XmlElement("Base")]
    public abstract string Code { get; set; }
}

public class Derived : Base
{
    [XmlElement("Derived")]
    public override string Code { get; set; }
}
2
  • Use the GetCustomAttribute(false) overload, to ensure "inherited" is set to false. And check the type of the Member, is it really the type 'Derived'. Your 'Member' must be the same as the PropertyInfo in your commented line. Commented Dec 19, 2019 at 11:22
  • Using GetCustomAttribute(false) does not change anything. Member in both cases seems to reference Base. Commented Dec 19, 2019 at 12:18

2 Answers 2

2

I can confirm this behaviour. You can combine the two methods with

    private static void Write<T1, T2>(Expression<Func<T1, T2>> expression)
    {
        var me = ((MemberExpression)expression.Body);

       Console.WriteLine(me.Expression.Type.GetProperty(me.Member.Name)
        .GetCustomAttributes<XmlElementAttribute>().First().ElementName);
    }

It seems like GetCusttomAttribute is accessing the BaseType, while GetCustomAttributes is returning all the properties in the class hierarchy. With "First" you just access the top-declaration.

Sign up to request clarification or add additional context in comments.

2 Comments

Hooray! This seems to do the trick. Alternatively, your other suggestion also helps. There is no need to invoke the plural version but do GetCustomAttribute<XmlElementAttribute>(false) instead.
Indeed this will work, nice find. There's still an issue though, if the given property has several attributes of the same type it may not always return the one you are looking for.
1

I can't realy figure out why your code doesn't work, it looks like the problem comes from automatic Expression translation from lambdas but here's something that works as you'd expect still using Expressions by doing nearly the same thing but manualy :

private static void Write(Expression expression)
{   
    Console.WriteLine(((MemberExpression)expression.Body).Member.GetCustomAttribute<XmlElementAttribute>().ElementName);
}

private static void Main(string[] args)
{
    Write(Expression.Property(Expression.New(typeof(Derived).GetConstructors()[0]), "Code"));
    Write(Expression.Property(Expression.TypeAs(Expression.New(typeof(Derived).GetConstructors()[0]), typeof(Base)), "Code"));
}

4 Comments

Looks to me that you copy pasted it from elsewhere? And there might be control characters in it.
I just copy pasted from Visual Studio as i always do, i don't understand why it wouldn't work this time, just rewrote the line without copy paste and it worked, thanks for giving me the idea
Even though your solution may work it's way too complicated and defeats the purpose. I wanted something elegant and also something that can be refactored at any time. Your solution offers resembles what the compiler should be doing in the first place.
Yeah I know what the issues of this code are :/ I don't think there's a way to make it work as you want it to because what you made should work imo. Maybe you should try to bring this issue to Microsoft's eyes. What's for sure is i'm not able to do more for you

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.