I found something interesting while playing around with System.ComponentModel.TypeDescriptor
:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime DateOfBirth { get; set; }
public Enum Enum { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var person = new Person();
var typeDescriptionProvider = TypeDescriptor.AddAttributes(person, new DescriptionAttribute("Some description of this person"));
// Returns no attributes if person is a *struct* instance,
// but does return the DescriptionAttribute if person is a *class* instance
--> var descriptionAttributes = TypeDescriptor.GetAttributes(person).OfType<DescriptionAttribute>().ToList();
// Always returns the DescriptionAttribute
var descriptionAttributesUsingTdp = typeDescriptionProvider.GetTypeDescriptor(person).GetAttributes().OfType<DescriptionAttribute>().ToList();
Console.ReadLine();
}
}
When the definition of Person
is a class, the line of code with the -->
finds and returns the DescriptionAttribute
just fine.
When I change Person
to a struct, the code does not find the DescriptionAttribute
.
What is the rationale behind this difference? Is there some magic going on for reference types that is absent for value types?
I know that the line of code below the -->
returns the attribute regardless of whether the instance of Person is a class or a struct. This is because the TypeDescriptionProvider
returned by the AddAttributes()
method call was the one that performed the addition.
Why doesn't TypeDescriptor.GetAttributes(person)
return the attribute when Person
is a struct?
TypeDescriptor.AddAttributes
and TypeDescriptor.GetAttributes
use reference equality to find objects in TypeDescriptor
's internal cache. If Person
is a class, then the person
arguments in those two method calls reference the same instance. However, if Person
is a struct, then the person
arguments get boxed independently into different instances, so GetAttributes
is unable to find the original person
.
If person
is only boxed once, then the code works as expected:
object person = new Person(); // `object` instead of `var`
Why does typeDescriptionProvider.GetTypeDescriptor(person).GetAttributes()
always return the attribute?
The TypeDescriptionProvider.GetTypeDescriptor(Object)
documentation isn't clear (at least to me), but note that you can apparently pass anything to typeDescriptionProvider.GetTypeDescriptor
— like 42
(a struct) or "Hello, World!"
(a class) — and get your attribute back:
var descriptionAttributesUsingTdp = typeDescriptionProvider.GetTypeDescriptor(42)
.GetAttributes().OfType<DescriptionAttribute>().ToList();
// descriptionAttributesUsingTdp.Count == 1
Based on this behavior, my guess is that the typeDescriptionProvider
returned by TypeDescriptor.AddAttributes(person, ...)
is not associated with the person
. Instead, it represents a plug-in that can add your attribute to any object, and GetTypeDescriptor
is the method that does so.