Can you get merged attributes for a class in C#? - c#

Say I have the following set of classes, is it possible for the attributes of DerivedClass to be merged? Currently if I use the GetType().GetCustomAttributes()method passing in true for inheritance it takes the highest attributes in the inheritance structure.
i.e. [Another("Bob")] and [My(16)]
Is it possible for the attributes to be merged? So I would end up with two attributes of [My(16, "Male")] and [Another("Bob")]
I don't mean to say that I would specify an attribute of [My(16, "Male")] but rather I would be returned an attribute with an Age of 16 and a Gender of Male.
public class MyAttribute : Attribute
{
public MyAttribute(int age)
{
Age = age;
}
public MyAttribute(int age, string gender)
{
Age = age;
Gender = gender;
}
public int Age { get; set; }
public string Gender { get; set; }
}
public class AnotherAttribute : Attribute
{
public AnotherAttribute(string name)
{
Name = name;
}
public string Name { get; set; }
}
[My(12, "Male")]
[Another("Bob")]
public class BaseClass
{
}
[My(16)]
public class DerivedClass : BaseClass
{
}

You can have multiple instances of the same attribute on an entity (this is an option on the AttributeUsageAttribute applied to your attribute). When you get attributes on a type, you can get all the attributes applied to the inheritance chain.
However there is nothing in the standard tools that will take two attribute instances and make one.
(In theory a post processor that re-wrote the MSIL could do this.)

First, I don't believe any of the behavior you're inquiring about would depend on the AttributeUsage attribute which should be decorating your attribute classes (it looks like I could be wrong). Nor do I see where any attribute would be getting merged. Each attribute is a representation of a class in it's own right. Each attribute class should return it's own data. I would expect DerivedClass.GetCustomAttributes() to return three separate references for each attribute - 2 from the base class, and 1 for itself.
Try setting the AttributeUsage on the 'MyAttribute' to allow multiple. It may not be merging them, but taking the 'latest' version of the attribute.
I just thought of something else that could be causing the mysterious disappearing act. You may have to go after the base class to get the original attribute? You could try that:
base.GetType().GetCustomAttributes();
I did some digging and here is what the AttibuteUsage will do and could be causing you to only see one of the MyAttribute classes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
I was close in my statement earlier about using AttributeUsage; however, it is the Inherited named parameter that will limit visibility to the base class' MyAttribute decoration. Try specifying Inherited = true and see what you get.

Related

Decide Value of custom attribute at runtime

I have the following scenario:
/* Attribute to be filled */
public class Flower
{
public FlowerType Type { get; set; }
public string Value { get; set; }
}
public enum FlowerType
{
Rose,
Daisy,
Tulip
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class FlowerAttribute : Attribute
{
public string FlowerValue { get; }
public FlowerAttribute(string flowerValue)
{
FlowerValue = flowerValue;
}
}
What I am trying to achieve is a way to give the class Flower an attribute depending on runtime, meaning that if the flower will be instantiated as a Rose type, then the attribute on top of Flower will have the FlowerValue set to "Rose". Can I achieve this somehow? Thanks as always in advance!
Can I achieve this somehow?
No. Attribute arguments are always decided at compile-time - that's why they have to be compile-time constants. The values are baked into the IL.
It's not clear what the bigger goal is here, but anything dynamic is unlikely to be a good fit for attributes.

C# custom attribute how to change target class property

I'm try to find a solution to realize decrypt/encrypt when access a class property by get/set.
I had researched something about C# custom attribute, but seemimgly it can't work, so I want to know whether it's impossible to change the class property by attribute.
[AttributeUsage(AttributeTargets.Property)]
public class MyAttribute : Attribute
{
// How to change the tragetClass.Name when access by get/set
}
public class TargetClass
{
[MyAttribute]
public string Name { get; set; }
}

Can extracting a base class break binary serialisation

If I have a class with a number of fields, and I serialise it using binary serialisation.
I then extract a base class and move some of the fields from the class into the base class. Eg.
class Class
{
int field1;
string field2;
}
Is changed to
class Class : BaseClass
{
int field1;
}
class BaseClass
{
string field2;
}
Is it possible for this to break serialisation in any way - i.e. will it always be possible to deserialise old versions of Class into new versions of Class and vice versa.
Experiments I performed indicated that this is fine, but if somebody knows of any edge cases where this wouldn't work, that would obviously be great before I put this into production code.
EDIT:
There is a problem while deserializing: the member "field1" will not be properly deserialized.
1) Serialized person:
var person = new Employee()
{
Name = "Mark Zuckerberg",
Salary = 1000
};
var bf = new BinaryFormatter();
bf.Serialize(new FileStream("C:\\TEMP\\test.dat", FileMode.Create), person);
[Serializable]
public class Employee
{
public string Name { get; set; }
public decimal Salary { get; set; }
}
2) Changed class structure
[Serializable]
public abstract class Person
{
public string Name { get; set; }
}
[Serializable]
public class Employee : Person
{
public decimal Salary { get; set; }
}
3) Deserialized: Name is null
Original Answer:
Assuming you are using the BinaryFormatter, this article does not mention anything about it, so I guess it's fine (EDIT: It's NOT fine).
Here are some best practices to follow generally (also extracted from above article):
Never remove a serialized field.
Never apply the NonSerializedAttribute attribute to a field if the
attribute was not applied to the field in the previous version.
Never change the name or the type of a serialized field.
When adding a new serialized field, apply the OptionalFieldAttribute
attribute.
When removing a NonSerializedAttribute attribute from a field (that
was not serializable in a previous version), apply the
OptionalFieldAttribute attribute.
For all optional fields, set meaningful defaults using the
serialization callbacks unless 0 or null as defaults are acceptable.
To ensure that a type will be compatible with future serialization engines, follow these guidelines:
Always set the VersionAdded property on the OptionalFieldAttribute
attribute correctly.
Avoid branched versioning.

How to use MEF Inherited Export & MetaData?

I have an interface:
[InheritedExport(typeof(IMetric))]
public interface IMetric { ... }
I have a Meta attribute interface:
public interface IMetricAttribute { ... }
and an attribute that implements it:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MetricAttribute : ExportAttribute, IMetricAttribute {
public string MetricName { get; set; }
public string MetricDescription { get; set; }
public MetricAttribute(string name, string description)
: base(typeof(MetricAttribute)) {
this.MetricName = name;
this.MetricDescription = description;
}
}
I then have two classes:
[Metric("MetricA","MetricA")]
public class MetricA: IMetric { ... }
[Export(typeof(IMetric))] <<<< THIS IS IMPORTANT
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }
I then try to import the metrics ( i can see both in the cataloge)
The following returns be MetricA AND MetricB
var metrics = compositionContainer.GetExports<IMetric>();
However the following returns ONLY MetricB and NOT MetricA
var metrics = compositionContainer.GetExports<IMetric, IMetricAttribute>();
any idea why?
(note the duplicate export on MetricB (it already has it from implementing IMetric))
thanks
David
First time I've seen this behaviour, but from what I can understand, metadata is generated per-export at the type level. So, given:
[Metric("MetricA", "MetricA")]
public class MetricA : IMetric
{
}
You have two exports for this type. You have the export of MetricA which is implictly provided by your MetricAttribute, and you have the inherited export for IMetric provided by the InheritedExport(typeof(IMetric)) attribute on your interface.
If you look at the container, you'll notice two exports defined for MetricA. Here is the first, with its metadata:
And here is the second:
You'll notice that the metadata is done on the export of MetricA, not the inherited export. If I added a further export, lets say [Export("test")] to MetricA, you get another export definition, with the same metadata items for MetricName and MetricDescription for the contract named "test". This shows you that as the type is analysed, the export attribute is identified, and the export definition that is created includes the metadata specified at the same level in the abstraction tree.
The easiest way to do what you want, is to drop out the InheritedExport, and modify your definition of your MetricAttribute to:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false), MetadataAttribute]
public class MetricAttribute : ExportAttribute, IMetricAttribute
{
public MetricAttribute(string name, string description)
: base(typeof(IMetric))
{
this.MetricName = name;
this.MetricDescription = description;
}
public string MetricName { get; private set; }
public string MetricDescription { get; private set; }
}
Where you then pass in typeof(IMetric) to the base ExportAttribute constructor. You then correctly get the two exports for GetExports<IMetric>() and GetExports<IMetric, IMetricAttribute>().
I came across the same problem and found a differen solution which worked fine for me:
I just added metadata to the interface!
[InheritedExport(typeof(IMetric))]
[Metric("name","description")]
public interface IMetric { ... }
You can leave the fields blank or use null as default, but it is important to specify the Metadata here.
Then you specify your classes without export attribute:
[Metric("MetricA")]
public class MetricA: IMetric { ... }
Be aware that you can can specify just one metadata, but the second one won't be description in this case, it will be null! So the metadata in the interface are NOT default values.
All in all this worked for me and I can use InheritedExport with my metadata :-)
To clarify on Matthew answer:
When you are defining custom metadata attribute class MetricAttribute and inheriting from ExportAttribute, you are essentially adding [Export] attribute to all classes that you decorate with your [Metric] attribute. This means that you don't need [InheritedExport] attribute on the interface anymore as it just creates separate export definition without any metadata.
If you want to create a more reusable metadata attribute, you can expose ExportAttribute constructor parameters in your MetricAttribute as follows:
public MetricAttribute(Type contractType, string name, string description)
: base(contractType)
{
this.MetricName = name;
this.MetricDescription = description;
}
By introducing contractType variable you can now supplement your definition of
[Export(typeof(IMetric))]
[Metric("MetricB", "MetricB")]
public class MetricB: IMetric { ... }
with:
[Metric(typeof(IMetric), "MetricB", "MetricB")]
public class MetricB: IMetric { ... }

Honouring of AttributeUsage on derived attribute types

Given the following, I would not expect the compiler to allow multiple attributes that are derived from the base attribute, given that is set to AllowMultiple=false. In fact it compiles without a problem - what am I missing here?
using System;
[AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
abstract class BaseAttribute : Attribute { }
sealed class DerivedAttributeA : BaseAttribute { }
sealed class DerivedAttributeB : BaseAttribute { }
class Sample1
{
[DerivedAttributeA()]
[DerivedAttributeB()]
public string PropertyA{ get; set; } // allowed, concrete classes differ
[DerivedAttributeA()]
[DerivedAttributeA()]
public string PropertyB { get; set; } // not allowed, concrete classes the same, honours AllowMultiple=false on BaseAttribute
}
The problem is simply that the AllowMultiple check only compares attributes of the same actual type (i.e. the concrete type instantiated) - and is perhaps best used with sealed attributes for this reason.
It will, for example, enforce the following (as an illegal duplicate), inheriting this from BaseAttribute:
[DerivedAttributeB()]
[DerivedAttributeB()]
public string Name { get; set; }
In short, I don't think you can do what you want here... (enforce no more than one instance including subclasses of BaseAttribute per property).
A similar example of this problem would be:
[Description("abc")]
[I18NDescriptionAttribute("abc")]
public string Name { get; set; }
class I18NDescriptionAttribute : DescriptionAttribute {
public I18NDescriptionAttribute(string resxKey) : base(resxKey) { }
}
The intent above is to provide a [Description] from resx at runtime (fully supported by ComponentModel etc) - but it can't stop you also adding a [Description].

Categories

Resources