I'm trying to get the Full Class description into a string value. I have designated the classes to Enumerate through, to get back the full description. However; calling ToString() on any class object only provides a summary.
I want the full definition, as in what is shown in the class file (.cs) related to it.
public class Expose : System.Attribute
{
public bool DoExpose;
public Expose(bool doExpose)
{
DoExpose = doExpose;
}
public static void DoStuff()
{
Assembly assembly = Assembly.GetAssembly(typeof(Expose));
var types = GetTypesWithExposeAttribute(assembly);
// get full class description - like the Car.cs
Console.WriteLine(types.First().FullName);
}
public static IEnumerable<Type> GetTypesWithExposeAttribute(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
if (type.IsDefined(typeof(Expose), true))
{
yield return type;
}
}
}
}
[Expose(true)]
public class Car
{
public int CarId { get; set; }
public string Name { get; set; }
public int Speed { get; set; }
}
Assuming you are asking about "xml documentation comments".
The comments are not part of metadata and can't be accessed via reflection.
If you need additional information to be available via reflection - use attributes. (I.e. C# documentation comments).
Related
I am looking for an elegant way of statically referencing a property attribute in C#. To give you an example, say I have this class:
public class A
{
[Attribute(Name="myAttributeName")]
public string Property1 { get; set; }
}
Now, I see the attribute as quite similar to a static member of a class, so in my mind, there should be an easy way to access the attribute from outside the class; e.g. through a similar operator to typeof or nameof (but it would return a list of attributes, since there may be multiple attributes to fetch). The way I would like to use this operator is as follows:
public class B
{
// Through an attribute definition
[Attribute2(attrof(A.Property1))]
public string Property2 { get; set; }
// In a method
public void method()
{
var attrs = attrof(A.property1);
}
}
I think I have found one way to make it work with two parameters like the example below - at least for the method invocation. Passing variables to attributes doesn't seem to work in C#, but that's nevertheless the way I'd like to construct my code.
public class C
{
public static object[] GetAttrs(Type type, string propertyName)
{
return type.GetProperty(propertyName).GetCustomAttributes(true);
}
}
public class A
{
[Attribute1(Name="myAttributeName")]
public string Property1 { get; set; }
}
public class B
{
// Through an attribute definition
// Unfortunately, passing variable to attrs not supported
// so this does not work
[Attribute2(C.GetAttrs(typeof(A), nameof(A.Property1)))]
public string Property2 { get; set; }
// In a method
public void method()
{
var attrs = C.GetAttrs(typeof(A), nameof(A.Property1));
}
}
However, it feels tedious to pass references to both the class and property, when syntactically, A.Property1 contains information about both - something a compiler should be able to draw information from. Therefore, I wonder if any such operator exists today, or if there are any other ideas on how this functionality could be achieved?
EDIT: I just thought about the B.Property2 attribute definition one more time and thought that it should still be possible to get this working, since I think attributes are constant. Or am I missing something here?
There is no default operator for such case, but you could implement something similar. Code to extract value from A.Property1 attribute is in Main function
using System;
using System.Reflection;
namespace ConsoleApp16
{
public class CustomAttribute : Attribute
{
public string Name { get; }
public CustomAttribute(string name)
{
Name = name;
}
}
public class ReferenceAttribute : Attribute
{
public string PropertyName { get; }
public Type Type { get; }
public ReferenceAttribute(Type type, string propertyName)
{
Type = type;
PropertyName = propertyName;
}
}
public class A
{
[Custom("text")]
public string Property1 { get; set; }
}
public class B
{
[Reference(typeof(A), nameof(A.Property1))]
public string Property { get; set; }
}
class Program
{
static void Main(string[] args)
{
var referenceAttribute = typeof(B).GetProperty(nameof(B.Property))
.GetCustomAttribute<ReferenceAttribute>();
var customAttribute = referenceAttribute.Type.GetProperty(referenceAttribute.PropertyName)
.GetCustomAttribute<CustomAttribute>();
Console.WriteLine(customAttribute.Name);
}
}
}
I have an object with multiple properties like this :
public int num1 { get; set; }
public int num2 { get; set; }
public string str1 { get; set; }
public string str2 { get; set; }
These properties are inside a class which is dynamically generated so it will erase all CustomAttributes on them.
I tried adding
[Submit]
MyClass myObject
but it didn't spread on my object properties
Is there a way to do that dynamically inside the c# ?
I'm having some trouble understanding your question, but let me attempt to clarify.
I have an object with multiple attributes
...
it will erase all CustomAttributes
In C# parlance, class members of the format <access> <type> <name> { get; set; } are referred to as "properties" rather than "attributes." "Attributes," on the other hand, are the C# implementation of annotations, such as the custom attributes to which you are referring.
That said, I currently understand you to mean you have an automatically generated class with multiple properties. You would like each of these properties to have their own custom attributes, but if you edit the class they are removed the next time it is generated, and you cannot get the class generator to include custom attributes.
It might be helpful to know more of the context of your class. How is it being generated, for example? If it is an Entity Framework class, the following SO question may provide some insight:
Add data annotations to a class generated by entity framework. In general, is (or can you make) the generated class partial? If so, then you can still follow the approach in the above question's answer, viz. make your own partial class implementation that provides the properties' custom attributes.
For example, if your generated class looks (or can be made to look) like this:
/// <auto-generated />
public partial class MyClass
{
public int Num1 { get; set; }
public int Num2 { get; set; }
public string Str1 { get; set; }
public string Str2 { get; set; }
}
You could write the other part of the partial class with the custom annotations, like this:
/// human generated
public partial class MyClass
{
[Submit]
public int Num1 { get; set; }
[Submit]
public int Num2 { get; set; }
[Submit]
public string Str1 { get; set; }
[Submit]
public string Str2 { get; set; }
}
Again, without knowing more about your situation, I am not certain if this provides you the information you need, but I hope it at least gives you a starting point.
Edit
If the class is not partial, you might consider wrapping your generated class with a class whose wrapping properties use the custom attribute. For example,
/// human generated
public class MyClassWrapper
{
private readonly MyClass wrapped;
public MyClassWrapper(MyClass wrapped)
{
this.wrapped = wrapped;
}
[Submit]
public int Num1 { get => this.wrapped.Num1; set => this.wrapped.Num1 = value; }
[Submit]
public int Num2 { get => this.wrapped.Num2; set => this.wrapped.Num2 = value; }
[Submit]
public string Str1 { get => this.wrapped.Str1; set => this.wrapped.Str1 = value; }
[Submit]
public string Str2 { get => this.wrapped.Str2; set => this.wrapped.Str2 = value; }
}
Edit 2
If you would rather have a more dynamic solution, at the cost of some design and runtime complexity, you might consider this SO question: How to add property-level Attribute to the TypeDescriptor at runtime?. It seems to address a similar concern --
Really, it's for MS's Application Settings that generates code, so you can't extend it in any way property-wise.
I won't duplicate Gman's explanation entirely here, but essentially this approach consists of
Get the type (MyClass) or an instance of the type myObject
Use TypeDescriptor.GetProvider(MyClass/myObject).GetTypeDescriptor(MyClass/myObject) to get the type or object's baseline ICustomTypeDescriptor
Construct his PropertyOverridingTypeDescriptor with this baseline descriptor
Iterate through MyClass/myObject's properties' definitions with TypeDescriptor.GetProperties(MyClass/myObject). Use TypeDescriptor.CreateProperty to create a new property definition based on the current property's definition, that adds the custom attribute EditorAttribute (or in your case SubmitAttribute), and use the PropertyOverridingTypeDescriptor constructed in 3. to use the new property definition.
Construct his TypeDescriptorOverridingProvider with the PropertyOverridingTypeDescriptor constructed in 3.
Apply the new property definitions to MyClass/myObject with TypeDescriptor.AddProvider
One more way to add attributes to a dynamically generated class is to add one more command line app to your code generation pipeline.
Here's an example how to rewrite C# code file with Microsoft.CodeAnalysis.CSharp library:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.IO;
namespace SB.Common.ContractGenerator
{
class SubmitAttributeAdder : CSharpSyntaxRewriter
{
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)=>
node.WithAttributeLists(
node.AttributeLists.Count == 0
? node.AttributeLists.Add(SyntaxFactory.AttributeList()
.AddAttributes(SyntaxFactory.Attribute(
SyntaxFactory.ParseName("Submit")))
// Add some whitespace to keep the code neat.
.WithLeadingTrivia(node.GetLeadingTrivia())
.WithTrailingTrivia(SyntaxFactory.Whitespace(Environment.NewLine)))
: node.AttributeLists);
}
class Program
{
static void Main(string[] args)
{
// The file name to be injected as a command line parameter
var tree = CSharpSyntaxTree.ParseText(
SourceText.From(File.ReadAllText("Test.cs")));
File.WriteAllText("Test.cs",
new SubmitAttributeAdder().Visit(tree.GetRoot()).ToString());
}
}
}
First, the input C# code file is parsed as the syntax tree, and then the SubmitAttributeAdder class skims through all declared classes and their properties to amend an attribute list for each of them.
After that the modified syntax tree is saved back to the same file.
Here the app only adds the Submit attribute in case the attribute list of a property is empty, but one can easily make the logic more sophisticated - e.g. check whether there are other attributes, add corresponding using <namespace> for the Submit attribute and so on.
An example Test.cs file before running the app:
namespace MyProject
{
class MyClass
{
public int num1 { get; set; }
public int num2 { get; set; }
public string str1 { get; set; }
public string str2 { get; set; }
}
}
...and after:
namespace MyProject
{
class MyClass
{
[Submit]
public int num1 { get; set; }
[Submit]
public int num2 { get; set; }
[Submit]
public string str1 { get; set; }
[Submit]
public string str2 { get; set; }
}
}
I am using MetadataType to define Json.NET attributes for the following type, then serializing it using Json.NET inside its ToString() method:
namespace ConsoleApp1
{
public interface ICell
{
int Id { get; }
}
public interface IEukaryote
{
System.Collections.Generic.IEnumerable<ICell> Cells { get; }
string GenericName { get; }
}
public sealed partial class PlantCell
: ICell
{
public int Id => 12324;
}
public sealed partial class Plant
: IEukaryote
{
private readonly System.Collections.Generic.IDictionary<string, object> _valuesDict;
public Plant()
{
_valuesDict = new System.Collections.Generic.Dictionary<string, object>();
var cells = new System.Collections.Generic.List<PlantCell>();
cells.Add(new PlantCell());
_valuesDict["Cells"] = cells;
_valuesDict["GenericName"] = "HousePlant";
}
public System.Collections.Generic.IEnumerable<ICell> Cells => _valuesDict["Cells"] as System.Collections.Generic.IEnumerable<ICell>;
public string GenericName => _valuesDict["GenericName"] as string;
public int SomethingIDoNotWantSerialized => 99999;
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this,
new Newtonsoft.Json.JsonSerializerSettings()
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
}
);
}
}
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[Newtonsoft.Json.JsonProperty]
public System.Collections.Generic.IEnumerable<ICell> Cells;
[Newtonsoft.Json.JsonProperty]
public string GenericName;
//...
}
}
class Program
{
static void Main(string[] args)
{
var plant = new Plant();
System.Console.WriteLine(System.String.Format("Output is {0}", plant.ToString()));
System.Console.ReadKey();
}
}
}
My problem is that Plant.ToString() will return '{}'. Why is that? It was working before. The only change I made was in PlantMetadata where I altered the MemberSerialization to OptIn instead of OptOut, as I had less properties I wanted included than left out.
As stated by Newtonsoft in this issue, MetadataTypeAttribute attributes are in fact supported by Json.NET. However, it appears that Json.NET requires that the MetadataClassType members must be properties when the corresponding "real" members are properties, and fields when the corresponding "real" members are fields. Thus, if I define your Plant type as follows, with two properties and one field to be serialized:
public sealed partial class Plant : IEukaryote
{
public System.Collections.Generic.IEnumerable<ICell> Cells { get { return (_valuesDict["Cells"] as System.Collections.IEnumerable).Cast<ICell>(); } }
public string GenericName { get { return _valuesDict["GenericName"] as string; } }
public string FieldIWantSerialized;
public int SomethingIDoNotWantSerialized { get { return 99999; } }
// Remainder as before.
Then the PlantMetadata must also have two properties and one field for them to be serialized successfully:
//Metadata.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public IEnumerable<ICell> Cells { get; set; }
[JsonProperty]
public string GenericName { get; set; }
[JsonProperty]
public string FieldIWantSerialized;
}
}
If I make Cells or GenericName be fields, or FieldIWantSerialized be a property, then they do not get opted into serialization.
Sample working .Net Fiddle.
Note that, in addition, I have found that the MetadataClassType properties apparently must have the same return type as the real properties. If I change your PlantMetadata as follows:
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public object Cells { get; set; }
[JsonProperty]
public object GenericName { get; set; }
[JsonProperty]
public object FieldIWantSerialized;
}
Then only FieldIWantSerialized is serialized, not the properties. .Net Fiddle #2 showing this behavior. This may be a Newtonsoft issue; as stated in the Microsoft documentation Defining Attributes in Metadata Classes:
The actual type of these properties is not important, and is ignored
by the compiler. The accepted approach is to declare them all as of
type Object.
If it matters, you could report an issue about the return type restriction to Newtonsoft - or report an issue asking that details of their support for MetadataTypeAttribute be more fully documented.
I have the following issue
Here is third party class which we are used(so i cannot change it)
public class ThirdPartyEmployee
{
public string F_Name { get; set; }
public string L_Name { get; set; }
public DateTime Date_of_birth { get; set; }
public string Telephone1 { get; set; }
public string Telephone2 { get; set; }
public string Position { get; set; }
//..... and so on
}
Also we have our own smaller and better Employee class
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
}
Sometimes we need to convert third party class to our own. There is extension method for it
public static class ThirdPartyExtensions
{
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
Now about the issue. If somebody consider to add some other properties to Employee class he\she can forget to change ConvertTo method. How we can avoid it ? Ideally i would like to have some compilation errors ...
Any suggestions ?
If your Employee class is just a container, there's one simple approach:
public class Employee
{
private readonly string firstName;
public Employee(string firstName)
{
this.firstName = firstName;
}
}
Now your conversion method has no choice but to pass all the arguments, so you get a compiler error when the conversion method isn't updated.
Of course, this still isn't foolproof - if you also care about changing the arguments, this doesn't help much.
And now that we have Roslyn, with great integration in Visual Studio, you can actually make your own compiler errors using a Roslyn analyzer. If you're not afraid of getting your hands dirty, this would be a great opportunity to show how useful something like that can be. Sadly, it's not very easy to use right now, and needs "the right kind of thinking" to be wielded well. It will allow you to make rules like "a class conversion extension method must assign all properties in the resulting class", for example.
You can not create a compilation error with standard means. There may be Visual Studio plugins that allow you to do that.
But it may not be necessary: You could change the CopyTo method so that instead of hardcoding all the properties to be copied, it uses reflection to obtain a list of all public properties to copy.
Example code to start with:
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
i.SetValue(destination, fi.GetValue(source));
}
To handle different property names: You could introduce an attribute which allows you to specify which property of ThirdPartyEmployee translates to which property of Employee. This can also be evaluated using reflection.
Example:
public class Employee
{
[CopyFromThirdPartyEmployee("F_Name")]
public string FirstName { get; set; }
[CopyFromThirdPartyEmployee("L_Name")]
public string LastName { get; set; }
[CopyFromThirdPartyEmployee("Telephone1")]
public string MobileTelephone { get; set; }
}
You could have the CopyTo method throw an exception when it finds a public property which does not have the required mapping attribute. That way you could be sure that every property also has the attribute - but that would be a runtime error, not a compile time error.
Another approach be to simply make Employee a wrapper for ThirdPartyEmployee:
public class Employee
{
private ThirdPartyEmployee _baseEmployee;
public Employee() { _baseEmployee = new ThirdPartyEmployee(); }
public Employee(ThirdPartyEmployee e) { _baseEmployee = e; }
public string FirstName
{
get { return _baseEmployee.F_Name; }
set { _baseEmployee.F_Name = value; }
}
...
}
That way you'd notice that if you can't access a property you haven't implemented it. The downside is that every employee would then be based on a ThirdPartyEmployee.
You can do this with the help of reflection, but dictionary for names mapping is needed:
public static class ThirdPartyExtensions
{
static Dictionary<string, string> map;
static ThirdPartyExtensions()
{
map = new Dictionary<string, string>{ {"F_Name", "FirstName"} /*and others*/};
}
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
if(map.Count < typeof(Employee).GetProperties().Count())
throw new Exception("Forget to add mapping for new field!");
foreach(var prop in typeof(ThirdPartyEmployee).GetProperties())
if(map.ContainsKey(prop.Name))
{
var temp = typeof(Employee).GetProperty(map[prop.Name]);
temp.SetValue(result, prop.GetValue(thirdPartyEmployee));
}
return result;
}
}
Using Roslyn analyzers it's possible to produce compile-time (and IntelliSense) errors that go beyond the scope of the compiler. Here's a very simple implementation of an analyzer that checks that all the properties of a type returned by a method have been assigned. It doesn't take into account control flow (e.g. if).
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AssignAllPropertiesAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("AssignAllPropertiesAnalyzer",
"All properties must be assigned.", "All properties of the return type must be assigned.", "Correctness",
DiagnosticSeverity.Warning, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
var methodNode = (MethodDeclarationSyntax)context.Node;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode);
if (methodSymbol.GetReturnTypeAttributes().Any(x => x.AttributeClass.Name == "AssignAllPropertiesAttribute"))
{
var properties = methodSymbol.ReturnType.GetMembers().OfType<IPropertySymbol>().Where(x => !x.IsReadOnly).ToList();
foreach (var assignmentNode in methodNode.DescendantNodes().OfType<AssignmentExpressionSyntax>())
{
var propertySymbol = context.SemanticModel.GetSymbolInfo(assignmentNode.Left).Symbol as IPropertySymbol;
if (propertySymbol != null)
{
properties.Remove(propertySymbol);
}
}
if (properties.Count > 0)
{
var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0]);
context.ReportDiagnostic(diagnostic);
}
}
}
The analyzer assumes an attribute named AssignAllProperties is applied to return type of a method. In the following example, ~~~~~~ marks the location where analyzer would produce a diagnostic.
class A
{
public string S { get; set; }
}
[return: AssignAllProperties]
public static A Create()
~~~~~~
{
return new A();
}
An analyzer can be installed both as a VSIX and as a NuGet package. I would recommend always using the NuGet approach - it would apply the analyzer for everyone consuming the code and would allow you to change the severity (e.g. to error), thus failing the compilation. To get started with building an analyzer library, install the Roslyn SDK and create an Analyzer with Code Fix C# project.
You cannot generate a compile error for that, but... I would move the conversion method to the Employee class. I suggest to avoid Extension methods that are dependend on data (like properties from other classes)
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
public static Employee From(ThirdPartyEmployee employee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
This way you keep all functionality in the right class/file and it is clear for others if they add properties.
I have a customer hierarchy like so:
abstract class Customer {
public virtual string Name { get; set; }
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
When I retrieve a Customer, I would like to show on the web form the properties to edit/modify. Currently, I use if statements to find the child customer type and show the specialized properties. Is there a design pattern (visitor?) or better way so I can avoid the "if" statements in presentation layer? How do you do it?
Further information: This is an asp.net website with nHibernate backend. Each customer type has its own user control on the page that I would like to load automatically given the customer type.
Can you use reflection to get the list of properties specific to an subclass (instance)? (Less error-prone.)
If not, create a (virtual) method which returns the special properties. (More error prone!)
For an example of the latter:
abstract class Customer {
public virtual string Name { get; set; }
public virtual IDictionary<string, object> GetProperties()
{
var ret = new Dictionary<string, object>();
ret["Name"] = Name;
return ret;
}
}
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Max spending"] = MaxSpending;
return ret;
}
}
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
public override IDictionary<string, object> GetProperties()
{
var ret = base.GetProperties();
ret["Award"] = Award;
return ret;
}
}
You probably want to create sections (fieldsets?) on your Web page, anyway, so if would come into play there, making this extra coding kinda annoying and useless.
I think a cleaner organization would be to have a parallel hierarchy of display controls or formats. Maybe use something like the Abstract Factory Pattern to create both the instance of Customer and of CustomerForm at the same time. Display the returned CustomerForm instance, which would know about the extra properties and how to display and edit them.
new:
interface CustomerEdit
{
void Display();
}
edit:
abstract class Customer {
protected CustomerEdit customerEdit; // customers have an object which allows for edit
public virtual string Name { get; set; }
public void Display() { customerEdit.Display(); } // allow the CustomerEdit implementor to display the UI elements
}
// Set customerEdit in constructor, tie with "this"
class HighValueCustomer : Customer {
public virtual int MaxSpending { get; set; }
}
// Set customerEdit in constructor, tie with "this"
class SpecialCustomer : Customer {
public virtual string Award { get; set; }
}
usage:
Customer whichCouldItBe = GetSomeCustomer();
whichCouldItBe.Display(); // shows UI depeneding on the concrete type
Have you tried something like this:
public class Customer<T>
where T : Customer<T>
{
private T subClass;
public IDictionary<string, object> GetProperties()
{
return subClass.GetProperties();
}
}
With a subclass of:
public class FinancialCustomer : Customer<FinancialCustomer>
{
}
This is off the top of my head so might not work. I've seen this type of code in CSLA.NET.
Here's the link to the CSLA.NET class called BusinessBase.cs which has a similar definition to what I've given above.