How to pass objects into an attribute constructor - c#

I am attempting to pass objects into an Attributes constructor as follows:
[PropertyValidation(new NullOrEmptyValidatorScheme())]
public string Name { get; private set; }
With this attribute constructor:
public PropertyValidationAttribute(IValidatorScheme validator) {
this._ValidatorScheme = validator;
}
The code won't compile. How can I pass an object into an attribute as above?
EDIT: Yes NullOrEmptyValidatorScheme implements IValidatorScheme.
The error: error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.

The values into attributes are limited to simple types; for example, basic constants (including strings) and typeof... you can't use new or other more complex code. In short; you can't do this. You can give it the type though:
[PropertyValidation(typeof(NullOrEmptyValidatorScheme)]
i.e. the PropertyValidation ctor takes a Type, and use Activator.CreateInstance inside the code to create the object. Note that you should ideally just store the string internally (AssemblyQualifiedName).
From ECMA 334v4:
§24.1.3 Attribute parameter types
The types of positional and named
parameters for an attribute class are
limited to the attribute parameter
types, which are:
One of the following types: bool, byte, char,
double, float, int, long, short, string.
The type object.
The type System.Type.
An enum type, provided it has public accessibility and the
types in which it is nested (if any)
also have public accessibility.
Single-dimensional arrays of the above
types.
and
§24.2 Attribute specification
...
An expression E is an
attribute-argument-expression if all
of the following statements are true:
The type of E is an attribute
parameter type (§24.1.3).
At compile-time, the value of E can be
resolved to one of the following:
A constant value.
A typeof-expression (§14.5.11) specifying a non-generic
type, a closed constructed type
(§25.5.2), or an unbound generic type
(§25.5).
A one-dimensional array of
attribute-argument-expressions.

As previous posters noted, the types use in attribute arguments are quite severely restricted (understandably, because their values need to be serialized directly into the assembly metadata blob).
That said, you can probably create a solution that utilizes typeofs, as those can be used.
For instance :
[PropertyValidation(typeof(NullOrEmptyValidatorScheme))]
public string Name { get; private set; }
This syntax is perfectly legal. The code that reads your attributes you have to get the validator type, create a new instance of the validator (it can even maintain a cache of validators keyed on valicator types, if appropriate - this is a fairly common technique), and then invoke it.

Also... (I think it is a Microsoft Bug)
You can't put a default value to "null" but default simple default value are ok ('false', '7', '"Test").
NExt example will give you the following error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
in file: ... \CSC
public class SampleAttribute : Attribute
{
private string _test;
public SampleAttribute(string test = null)
{
_test = test;
}
}
[Sample]
public class Toto
{
}

Related

Covariance error on Valued Type interface

I have a generic interface holding a covariant TValue parameter and an abstract class that does some repetitive stuff to liberate the child classes from that burden. Then I have 2 subclasses that extend from that abstract one, the first setting the generic parameter as a string and the second as an int.
This is a subpiece of code taken from the project, overly simplified just to focus on the matter.
public interface IElement<out TValue>
{
string Name { get; }
TValue Value { get; }
}
public abstract class Element<TValue> : IElement<TValue>
{
public string Name { get; }
public TValue Value { get; set; }
public Element(string name)
{
Name = name;
}
}
public class Name : Element<string>
{
public Name() : base("Name") { }
}
public class Height : Element<int>
{
public Height() : base("Height") { }
}
Basically - and this is not what I'm doing in my code, but illustrates fairly simply the problem I'm having - if I try to assign Name to an IElement holding object like this:
IElement<object> element = new Name();
It succeeds as I had expected since the TValue parameter in IElement is covariant.
However if I set it to Height:
IElement<object> element = new Height();
I get an Cannot implicitly convert type 'Height' to 'IElement<object>'. An explicit conversion exists (are you missing a cast?) error.
Now, I don't know why this works with a class that sets the generic parameter as a string, but not with a an int (or enum as I have also in the project some enums). Is it because string is a class and int is a struct?
Any help is greatly appreciated.
Simply, because one is a value type. The CLR disallows it as it will need to preserve its identity, whereas boxing does not.
Eric Lippert has a great blog about this at Representation and identity
covariant and contravariant conversions of interface and delegate
types require that all varying type arguments be of reference types.
To ensure that a variant reference conversion is always
identity-preserving, all of the conversions involving type arguments
must also be identity-preserving. The easiest way to ensure that all
the non-trivial conversions on type arguments are identity-preserving
is to restrict them to be reference conversions.
Additionally, you can read a lot more about identity, conversion, generics and variance in the specs at various places
11.2.11 Implicit conversions involving type parameters
For a type-parameter T that is not known to be a reference type
(§15.2.5), the following conversions involving T are considered to be
boxing conversions (11.2.8) at compile-time. At run-time, if T is a
value type, the conversion is executed as a boxing conversion. At
run-time, if T is a reference type, the conversion is executed as an
implicit reference conversion or identity conversion.

How to use the `dynamic` when specifying generic type arguments in C#?

How to use the dynamic when specifying generic type arguments in C#?
I am reading the CLR via C# book. And I come across the following paragraph:
It is also possible to use dynamic when specifying generic type arguments to a generic class
(reference type), a structure (value type), an interface, a delegate, or a method. When you do this, the
compiler converts dynamic to Object and applies DynamicAttribute to the various pieces of
metadata where it makes sense. Note that the generic code that you are using has already been
compiled and will consider the type to be Object; no dynamic dispatch will be performed because the
compiler did not produce any payload code in the generic code.
As far as I understand this excerpt tells that I can use the dynamic as a type argument in (e.g.) a class definition. But after trying this out I come to a conclusion that it is no different from using any other placeholder in the type argument. So, I doubt that my understanding is correct.
using System;
namespace myprogram
{
class A<dynamic> {
public dynamic a;
}
class B {
public Int32 b;
}
class C<T> {
public T c;
}
class Program {
static void Main(string[] args) {
//Cannot implicitly convert type 'string' to 'myprogram.B' [Console.NET]csharp(CS0029)
//A<B> a = new A<B> {a = "foo"};
//Cannot implicitly convert type 'string' to 'myprogram.B' [Console.NET]csharp(CS0029)
//C<B> c = new C<B> {c = "foo"};
//as you can see it does not matter if I use the T or dynamic, the effect is the same
//so, what is the use of using the dynamic in the class definition?
}
}
}
It is very important to understand the difference between type "arguments" and type "parameters".
Consider this:
class Foo<T> { } // "T" is a type parameter
...
Foo<int> f; // "int" is a type argument
Type parameters declare what types you can pass to this generic type/method. Type parameters are essentially identifiers, not an existing type. When you pass a type to a generic type/method, the type you passed is called the type argument. This is quite similar to the difference between a method parameter and argument.
So the excerpt is trying to say that given a generic type, you can pass the type dynamic to it, and it will be treated as object by the CLR. It doesn't mean that you can do this:
class A<dynamic> {
}
It means that you can do this:
var list = new List<dynamic>();
Or with the types declared in your code:
C<dynamic> c = new C<dynamic> {c = "foo"};
Short answer: in your code dynamic is just a type parameter name, you're not actually passing an argument.
As far as I understand this excerpt tells that I can use the dynamic as a type argument in (e.g.) a class definition.
There are no type arguments in a class definition. In the definition of a generic type there are type parameters. When you construct a generic type these are type arguments. So this:
class A<dynamic>
{
}
var a = new A<string>();
is a generic type with one type parameter whose name is dynamic. Then follows an instantiation of the type where string is passed as a type argument to the type parameter dynamic. This:
class A<T>
{
}
var a = new A<dynamic>();
is a generic type with one type parameter whose name is T. Then follows an instantiation of the type where dynamic is passed as a type argument to the type parameter T.
You're looking for the latter, not the former.
You can use dynamic as an Argument, so you can use
var c = new C<dynamic>();
But you cannot use a concrete Type, to build a generic Type, like
class A<dynamic> { }
class B<int> { }
In underline: This you should NOT do ! You are defining an argument name here !
Actually it doesn't cause compile-error, but the word "int" as treated as a parameter name, same as there would be T. It's a good paradigm to use names starting with T for generic type parameters, not to mix it up, with any type from the Rest of your program.
As you concluded yourself, your definition of A and C are completly identical,
except you are confused, cause the word dynamic has nothing to do with the type
dynamic in this place.
If you want to assign a string, of course you have to create a new C<string>() or new C<object>() or any other type accepting a string.
In my case I was in reality masking an ExpandoObject as a dynamic, so I ended up using code like this:
static async Task<TReturn> GenericMethodAsync<TReturn()
{
if (typeof(TReturn) == typeof(ExpandoObject))
// Return an ExpandoObject
}
Which was then used by a method like this one
static async Task<dynamic> OtherMethodAsync()
{
return await GenericMethodAsync<ExpandoObject>();
}

How to restrict derived class to only have members of certain types

I need to enforce the members of a class derived from a base class to only have a certain type.
For example if I have a MustInherit Class Serializable which has a method ToByteArray().
How can I restrict derived classes to only have fields which are specific types (for example restricted to having fields of type int, char or byte) so that I'm guaranteed to be able to serialize the class with the ToByteArray method which I have written.
The reason for this is because I want any derived classes to have a constant byte representation when it is serialized.
For example:
class Foo
Inherits Serializable
'Valid field as it is a char guaranteed to be two bytes
Public field1 As Char
'Valid field as it is a Int which is guaranteed to be two bytes
Public field2 As Integer
'**Invalid as a string can be a dynamic length which is not wanted in this context
Public field3 As String
End Class
Is this restriction possible in .net? vb.net in specific however I'm familiar with both and can convert between the two.
An idea I had was to throw a run-time error in the class Serializable's constructor after looping through the fields using Reflection and comparing their types against a list of valid types. However this a is a weak solution as an invalid class would only be detected during run-time, rather than compile time.
Here is a SO question which tackles the same problem but is in C++ not C# (just for guidance as to the desired behavior if needed)
How to enforce derived class members on derived classes
Update 9/2/2020 now that I've learned Unit Tests:
Quite a simple solution is to use Reflection in a UnitTest
<TestMethod()>
Public Sub ShouldObeyTypeConstraintsTest()
Dim types As IEnumerable(Of Type) = From t In Assembly.GetAssembly(GetType(IRestricted)).GetTypes()
From i In t.GetInterfaces()
Where i.IsGenericType AndAlso (i.GetGenericTypeDefinition() Is GetType(IRestricted(Of )))
Select t
For Each type As Type In types
Dim info() As FieldInfo = serializable.GetFields()
For Each field In info
Dim fieldType As Type = field.FieldType()
If Not isValidType(fieldType) Then
Assert.Fail($"IRestricted type {serializable.FullName} has invalid field {fieldType.Name}")
End If
Next
Next
End Sub
No, what you're describing is not possible. There is no compile-time constraint you can place on a class that dictates what types of members it can have.
If you're going to implement this custom ToByteArray method, one solution would be to use attributes that indicate which members are or are not serialized. There's already an attribute for that to determine which fields are included in binary serialization. That's the NonSerializedAttribute.
In your ToByteArray() method, as you're reflecting the various fields, you would check each one to see if it has the attribute, and ignore it if it does. (I'm assuming that you're using reflection, because otherwise the method would specify which fields to include, and the problem wouldn't exist.)
If you're going to use the existing attribute, you might want to just consider using existing binary serialization instead of writing your own ToByteArray method. You can use the BinaryFormatter class. Then you can use the same attribute to prevent serializing certain fields.
What none of this gets you is compile-time certainty that the type can actually be serialized. Because serialization will involve types (not logic that depends on particular values) any serialization issues would appear as you test. You could even write unit tests that create and serialize instances of each class and that would prevent runtime errors.
You can use several override methods with the types you want to set the value of the MyField just by one of them.
In this way you can be sure the MyField type is one of your specified types.
using System;
public class Program
{
public static void Main()
{
var b = new Book();
b.SetMyField(1);
Console.WriteLine(b.MyField);
b.SetMyField('A');
Console.WriteLine(b.MyField);
}
}
public interface IMySerializable {
object MyField { get; }
void SetMyField( int val);
void SetMyField( char val);
void SetMyField( byte val);
}
public class Book : IMySerializable {
public object MyField {
get;
private set;
}
public void SetMyField( int val){
MyField=val;
}
public void SetMyField( char val){
MyField=val;
}
public void SetMyField( byte val){
MyField=val;
}
}
Unfortunately you can not specify some types like int, char in the where condition of a generic class or method in C# else you get an error like this:
Also try using the ISerializable interface. Here there is an article about it.

C# reflection generic property type

Say that I have a class like this:
public class TestClass <T>
{
public T Prop { get; set; }
}
I'm trying to identify that the property type is T, not the actual type that is passed e.g. int
In order to clarify it a bit more:
TestClass<int> tc=new TestClass<int>();
tc.GetType().GetProperty("prop").PropertyType.Name; //this returns int, but I need "T"
When you create a TestClass<int> instance, you have a reified generic type - the property is int, not T.
To get at the actual generic type, you can use GetGenericTypeDefinition:
var genericType = tc.GetType().GetGenericTypeDefinition();
var typeName = genericType.GetProperty("Prop").PropertyType.Name;
And if you want to distinguish between actual types and generic type arguments, you can use Type.IsGenericParameter:
genericType.GetProperty("Prop").PropertyType.IsGenericParameter // true
Note that if you are are using C# 6, and require the generic type argument's name within it's defining class, you can get away without using reflection at all:
var name = nameof(T); // "T"
If you need the the generic type argument's name outside of it's class, you will need to use reflection (see Luaan's answer.)

How do you put a Func in a C# attribute (annotation)?

I have a C# annotation which is :
[AttributeUsage(AttributeTargets.Method)]
public class OperationInfo : System.Attribute {
public enum VisibilityType {
GLOBAL,
LOCAL,
PRIVATE
}
public VisibilityType Visibility { get; set; }
public string Title { get; set; }
public Func<List<string>, List<string>> Func;
public OperationInfo(VisibilityType visibility, string title, Func<List<string>, List<string>> function) {
Visibility = visibility;
Title = title;
Func = function;
}
}
As you can see, there is a property which is a Func and I want to call it dynamically. Basically, I'd like to parse all the methods which have this annotation and then call the Func binded to the annotation.
I'd like to use it like this (this is a simple example of an echo function, which gets a string and return the same string):
[OperationInfo(OperationInfo.VisibilityType.GLOBAL, "echo", IDoEcho)]
public static string DoEcho(string a)
{
return a;
}
[OperationInfo(OperationInfo.VisibilityType.PRIVATE, null, null)]
public static List<string> IDoEcho(List<string> param) {
return new List<string>() { DoEcho(param[0]) };
}
I've got no error in my Annotation class but when it comes to regenerate the entire solution, each time I declare a method with the annotation I've got an error which tells me that I must use literals in an annotation.
I understand there is limitations, but is there any way I can avoid this problem? I know I can use a string instead of a Func, and look dynamically for the function whose name is the same as the string, but I would like not to do so.
Thanks for helping :)
From the C# spec:
17.1.3 Attribute parameter types
The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
· One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
· The type object.
· The type System.Type.
· An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2).
· Single-dimensional arrays of the above types.
I don't think it is possible, c# only allows you to use Attributes with constant values only.
One way of doing this would be to create a class instead of a func, and then you pass the type of that class into the attribute.
You'd then need to instantiate it with reflection and execute a method in it.
So an example would be:
public interface IDoStuff
{
IList<string> Execute();
}
then your attribute would take a type instead of a Func
public OperationInfo(VisibilityType v, string title, Type type)
{
///...
}
you then would create a class that implements your interface and pass it into the attribute. Although this would mean that (at least initially, and without knowing your problem) you will need to create a new class every time you want to return different results.

Categories