22

I don't think it's possible but since I haven't got a definite clarity from MSDN, I feel that it's best to ask. Suppose that we have a class as follows.

public partial class Hazaa
{
  public int Shazoo { get; set; }
}

Then, I'd like Shazoo to be attributed as SuperCool but I wish to do so in another file. Since I'm using partial classes, I can add new properties as follows.

public partial class Hazaa
{
  [SuperCool]
  public int Wheee { get; set; }
}

But can I attribute a property declared in the first sample by writing code in the latter? I doubt it's possible but I'll be glad to stand corrected. If so, what's the syntax?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438

4 Answers4

32

Based on your requirements, as an option you can consider using:

Note: The attributes that you can register this way are not really your class attributes, but most frameworks like ASP.NET MVC use them like your class native attributes.

If you want to add data annotations attributes, specially in as ASP.NET MVC project, you will find this way helpful.

Also for other frameworks like Windows Forms that don't support MetadataTypeAttribute you can simply add support using AssociatedMetadataTypeTypeDescriptionProvider.

The solution is not restricted to data annotations attributes and you can use all kind of attributes that are meaningful for your libraries and frameworks.

How to define additional attributes?

You can create a metadata class that contains properties of your original class decorated by suitable attributes and then decorate the partial class by MetadataType attribute and introduce the metadata class for your original class.

How to see the impact of those attributes?

Frameworks like ASP.NET MVC use those attributes like they are defined in your original class.

Also You can register AssociatedMetadataTypeTypeDescriptionProvider as provider for your original type for other frameworks or components that may want to use TypeDescriptor to get information about your type.

Are they really my class attributes?

Please pay attention, this way, the attributes really don't belong to your original class, but for most frameworks, like ASP.NET MVC or Windows Forms that use TypeDescriptor to get information about types, they act like your class original attributes.

So if you want to get attributes for a property using reflection, you can't see them, but if you you use TypeDescriptor mechanism, you can see them.

An Example

Hazaa Class:

public partial class Hazaa
{
    public int Shazoo { get; set; }
}

HazaaMetadata Class

[MetadataType(typeof(HazaaMetadata))]
public partial class Hazaa
{
}

public class HazaaMetadata
{
    [DisplayName("Shazoo Name")]
    public int Shazoo { get; set; }
}

ASP.NET MVC Usage

you don't need to do anything else to make that DisplayName work, you can simply use Html.Labelfor or Html.DisplayNameFor to see the impact. It will show "Shazoo Name" as label text.

Windows Forms Usage

Some where in your application (like form load, main, ...) register the provider this way:

var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Hazaa));
TypeDescriptor.AddProvider(provider, typeof(Hazaa));

And as a result, you will see PropertyGrid and DataGridView use "Shazoo Name" as caption for property and column title.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    This's way too extensive to be useful in my case. Nevertheless, it seems like an interesting contribution and, without actually having tested it, I take your word for it, hehe. +1 for an ambitious approach, definitely! – Konrad Viltersten Dec 27 '15 at 17:08
  • Thank you for your feedback, this approach is heavily used in ASP.NET MVC or in solutions that use code generation. This way you can add some attributes to a class without touching the class itself, specially in cases that the main class has been generated using a tools (t4, ef, ...). – Reza Aghaei Dec 27 '15 at 17:09
  • It is? I'm actually doing ASP.NET right now and the autogenerated code is both awesome and terrible. Perhaps I'll end up using your suggestion anyway but being human, I prefer to most of stuff to be created for me, hehe. – Konrad Viltersten Dec 27 '15 at 17:12
  • 1
    If you are using ASP.NET MVC probably you will use `[MetadataType(typeof(HazaaMetadata))]` and this way, you don't need to register provider because DataAnnotation Metadata Provider of MVC looks for your `HazaaMetadata` class and will use it. It's so simple and lovely :) – Reza Aghaei Dec 27 '15 at 17:17
  • This solution is great when used in ASP.NET MVC, but there are some limitations. If for some reason you need to manually check the attributes of a property in code, c# will happily inform you that there are none (at least none of the ones included through the use of `MetadataType` attribute). – jahu Jun 01 '16 at 12:28
  • @jahu When you say *manually check attributes of a property in code* probably you mean using *reflection*. Yes, reflection, by default, doesn't know anything about such attributes, but you can check existence of a `MetadataType` attribute and extract additional information this way. Furthermore you don't need to use reflection to extract metadata about your types and their and properties. `TypeDescriptor` is the main mechanism of .Net Framework for providing metadata about types. – Reza Aghaei Jun 01 '16 at 18:28
  • @jahu After registering type descriptor (the same way I did in *Windows Forms Usage* part of answer) then you can simply get a `PropertyDescriptor` from `TypeDescriptor` and ask about `Attributes` and you will see it returns those properties too. Using `TypeDescriptor` is the right .Net way when you have a question about metadata of a type. – Reza Aghaei Jun 01 '16 at 18:31
  • @jahu I learned it in ASP.NET MVC but I use this mechanism in Windows Forms too. To see it in action which it's not limited to `ASP.NET MVC`, read **Windows Forms Usage** part of answer. If you follow the instructions in the answer you will see `PropertyGrid` and `DataGridView` will use `MetadataType` attribute. Always this is the framework which makes these attributes useful for us, by default, attributes doesn't perform anything for us, but they add some metadata to our types, then frameworks like MVC or WinForms or your hand-made framework or mechanisms make them useful. – Reza Aghaei Jun 01 '16 at 18:33
4

No, you can't.

You can only attach attributes to members you declare there and then, and unless the member is also declared as partial (so that you may reimplement it elsewhere) you cannot attach attributes to members declared in another partial file.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • (1) Darn... (2) Partial **member**? The first part is, as you surely realized, autogenerated. Can I force it to be partial from the file that I'm in control of **without** the autogeneration being altered? (3) +1 for brevity. – Konrad Viltersten Dec 27 '15 at 13:40
  • Doubtful, there are special syntax requirements for implementing a partial member, like having to specify the `partial` keyword and no access modifier. – Lasse V. Karlsen Dec 27 '15 at 13:41
  • There is no such this as a `partial` member, the other answers using the `[MetadataType]` attribute seem to be the closest you can get to that – mark.monteiro Nov 06 '20 at 18:00
2

of course you can do it using Metadata as follow:

public partial class Hazaa : EntityBase
{
    public int Shazoo { get; set; }
}

[MetadataTypeAttribute(typeof(HazaaMetadata))]
public partial class Hazaa : EntityBase
{
    internal sealed class HazaaMetadata
    {
        [SuperCool]
        public int Shazoo { get; set; }
    }
}
Okba
  • 29
  • 1
0

The best we could to to ensure decimals serialized to 2 decimal places was tp:

  1. Use XlmAttributeOverides to ignore the decimal filed i.e not serialze it

var xmlAttributeOverrides = new XmlAttributeOverrides(); var ctrlSumAttributes = new XmlAttributes { XmlIgnore = true }; xmlAttributeOverrides.Add(typeof(Sepa.GroupHeader39), "CtrlSum", ctrlSumAttributes); var ser = new XmlSerializer( typeof( Sepa.Document ), xmlAttributeOverrides);

  1. Use a partial class to add a string serialization of the ignored field

public partial class GroupHeader39 { [XmlElement("CtrlSum")] public string CtrlSumString { get => CtrlSum.ToString("F2"); set { /* required to work */ } } }

I hope this helps someone out with this issue.

s6521d
  • 1
  • 1