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

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.

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.

Implicit conversion fails when changing struct to sealed class

Struct/class in question:
public struct HttpMethod
{
public static readonly HttpMethod Get = new HttpMethod("GET");
public static readonly HttpMethod Post = new HttpMethod("POST");
public static readonly HttpMethod Put = new HttpMethod("PUT");
public static readonly HttpMethod Patch = new HttpMethod("PATCH");
public static readonly HttpMethod Delete = new HttpMethod("DELETE");
private string _name;
public HttpMethod(string name)
{
// validation of name
_name = name.ToUpper();
}
public static implicit operator string(HttpMethod method)
{
return method._name;
}
public static implicit operator HttpMethod(string method)
{
return new HttpMethod(method);
}
public static bool IsValidHttpMethod(string method)
{
// ...
}
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
return _name.GetHashCode();
}
public override string ToString()
{
return _name;
}
}
The following code triggers the issue:
public class HttpRoute
{
public string Prefix { get; }
public HttpMethod[] Methods { get; }
public HttpRoute(string pattern, params HttpMethod[] methods)
{
if (pattern == null) throw new ArgumentNullException(nameof(pattern));
Prefix = pattern;
Methods = methods ?? new HttpMethod[0];
}
public bool CanAccept(HttpListenerRequest request)
{
return Methods.Contains(request.HttpMethod) && request.Url.AbsolutePath.StartsWith(Prefix);
}
}
The compiler error is created by changing the HttpMethod struct into a sealed class. The error is reported for return Methods.Contains(request.HttpMethod), note: request.HttpMethod in this case is a string. Which produces the following:
Error CS1929 'HttpMethod[]' does not contain a definition for 'Contains' and the best extension method overload 'Queryable.Contains<string>(IQueryable<string>, string)' requires a receiver of type 'IQueryable<string>'
My question is why? I can redesign the code to make it work, but I'm wanting to know why changing from struct to sealed class creates this weird error.
Edit: Adding a simplified set of example code (available here: https://dotnetfiddle.net/IZ9OXg). Take note that commenting out the implicit operator to string on the second class allows the code to compile:
public static void Main()
{
HttpMethod1[] Methods1 = new HttpMethod1[10];
HttpMethod2[] Methods2 = new HttpMethod2[10];
var res1 = Methods1.Contains("blah"); //works
var res2 = Methods2.Contains("blah"); //doesn't work
}
public struct HttpMethod1
{
public static implicit operator HttpMethod1(string method)
{
return new HttpMethod1();
}
public static implicit operator string (HttpMethod1 method)
{
return "";
}
}
public class HttpMethod2
{
public static implicit operator HttpMethod2(string method)
{
return new HttpMethod2();
}
//Comment out this method and it works fine
public static implicit operator string (HttpMethod2 method)
{
return "";
}
}
Things I know:
Plainly the problem is in type inference.
In the first case, T is deduced to be HttpMethod1.
In the struct case, there is no conversion from HttpMethod1[] to IEnumerable<string> because covariance only works on reference types.
In the class case, there is no conversion from HttpMethod2[] to IEnumerable<string> because covariance only works on reference conversions, and this is a user-defined conversion.
Things I suspect but need to confirm:
Something about the slight difference between my last two points is confusing the type inference algorithm.
UPDATE:
It has nothing to do with covariant array conversions. The problem repros even without array conversions.
It does however have to do with covariant interface conversions.
It has nothing to do with strings. (Strings are often a bit weird because they have a hard-to-remember conversion to IEnumerable<char> that occasionally messes up type inference.)
Here's a program fragment that displays the problem; update your conversions to convert to C instead of string:
public interface IFoo<out T> {}
public class C {}
public class Program
{
public static bool Contains<T>(IFoo<T> items, T item)
{
System.Console.WriteLine(typeof(T));
return true;
}
public static void Main()
{
IFoo<HttpMethod1> m1 = null;
IFoo<HttpMethod2> m2 = null;
var res1 = Contains(m1, new C()); //works
var res2 = Contains(m2, new C()); //doesn't work
}
}
This looks like a possible bug in type inference, and if it is, it is my fault; many apologies if that is the case. Sadly I do not have time to look into it further today. You might want to open an issue on github and have someone who still does this for a living look into it. I would be fascinated to learn what the result was, and if it turns out to be a bug in either the design or the implementation of the inference algorithm.
Firstly, this is an observed behavioural difference between structs and classes. The fact that you have 'sealed' your class does not affect the outcome in this scenario.
Also we know the following statement will compile as expected for HttpMethod type declared as both a struct and class, thanks to the implicit operator.
string method = HttpMethods[0];
Dealing with Arrays introduces some lesser understood compiler nuances.
Covariance
When HttpMethod is a class (reference type), with an array such as HttpRoute.HttpMethods Array covariance (12.5 C# 5.0 Language Spec) comes into play that allows HttpMethod[x] to be treated as an object. Covariance will respect inbuilt implicit reference conversions (such as type inheritance or conversion to object) and it will respect explicit operators, but it will not respect or look for user defined implicit operators. (While a bit ambigous the actual spec doc lists specifically default implicit operators and explicit operators, it does not mention the user defined operators but seeing everything else is so highly specified you can infer that user defined operators are not supported.)
Basically Covariance takes precedence over many generic type evaluations. More on this in a moment.
Array covariance specifically does not extend to arrays of value-types. For example, no conversion exists that permits an int[] to be treated as an object[].
So when HttpMethod is a struct (value type), covariance is no longer an issue and the following generic extension from System.Linq namespace will apply:
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value);
Because you have passed in a string comparator, the Contains statement will be evaluated as follows:
public static bool Contains<string>(this IEnumerable<string> source, string value);
When HttpMethod is a class (Reference Type), thanks to covariance, HttpMethod[] in it's current form comparable only with Object[] and thus IEnumerable, but not IEnumerable< T >, Why not? because the compiler needs to be able to determine the type to generate the generic implementation of IEnumerable< T > and to determine if it can perform an explicit cast from object to T.
Put another way, Compiler cannot determine if T can definetly be a String or not, so it doesn't find the match in the Linq extension methods that we were expecting.
So what can you do about it? (! Not this !)
The first common attempt might be to try using .Cast< string >() to cast the HttpMethod instances to strings for the comparison:
return HttpMethods.Cast<string>().Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);
You will find that this does not work. Even though The parameter for Cast< T > is of type IEnumerable, not IEnumerable< T >. It is provided to allow you to use older collections that do not implement the generic version of IEnumerable with LINQ. Cast< T > is only designed to convert non-generic objects to their "true" type through the process of evaluating common origins for reference types or Un-Boxing for value types. If Boxing and Unboxing (C# Programming Guide) only applies to value types (structs) and since our HttpMethod type is a reference type (class) the only common origin between HttpMethod and String is Object. On HttpMethod there is no implicit, or even explicit operator that accepts Object and as it is not a value type there is no in built un-box operator that the compiler can use.
Note that this Cast<> will fail at runtime in this scenario when HttpMethod is a value type (class) the compiler will be happy to let it build.
Final Workaround
Instead of Cast< T > or relying on implicit conversions we will need to force the elements in the HttpMethods array to be explicitly cast to string (This will still use out implicit operator!) but Linq again makes this a trivial, but necessary task:
return HttpMethods.Select(c => (string)c).Contains(request.Method) && request.Url.AbsolutePath.StartsWith(Prefix);

Convert the user defined type to string and string to user defined type

Why .NET does not provide implicit or explicit conversion converting from string to the defined type and from the defined type to the string?
Example:
public class MyClass
{
public int Id;
public MyClass()
{
}
}
I can do:
var myClass = new MyClass() {Id=1};
string myClassString = myClass.ToString();
WHY I CANNOT DO?:
var myClassConverted = (MyClass) myClassString ;
Is there any serialization pattern exist can to that?
.ToString() is just a method , it can return any String value, this does not convert a class to a String.
We already have some questions about converting class to text:
Create an instance of a class from a string
C# Convert dynamic string to existing Class
Convert class to string to send via email
Converting Class to XML to string
Personally I use more the XML serialization approach, is very easy to serialize and deserialize and works very well with external services, like REST or SOAP.
ToString() is a method defined on the Object class which returns a new string instance and is not a type conversion.
There is no conversion that can be used to cast a String to your class but you can define your own custom conversion operator.
public class MyClass
{
public int Id;
public MyClass()
{
}
public static explicit operator MyClass(string s)
{
MyClass temp = new MyClass() { Id = Int32.Parse(s) };
// you should handle exceptions when string is not convertible to int
return temp;
}
}
You can then use your conversion:
MyClass c = (MyClass)("1");
From MSDN:
C# enables programmers to declare conversions on classes or structs so that classes or structs can be converted to and/or from other classes or structs, or basic types. Conversions are defined like operators and are named for the type to which they convert. Either the type of the argument to be converted, or the type of the result of the conversion, but not both, must be the containing type.
Conversion operators have the following properties:
Conversions declared as implicit occur automatically when it is required.
Conversions declared as explicit require a cast to be called.
All conversions must be declared as static.
You can find more on MSDN.
Quote from msdn Object.ToString Method :
The default implementation of the ToString method returns the fully qualified name of the type of the Object, as the following example shows.
using System;
public class Example
{
public static void Main()
{
Object obj = new Object();
Console.WriteLine(obj.ToString());
}
}
// The example displays the following output:
// System.Object
.ToString() does not contain any unique information of your current object so you can not reconstruct the object from this string.
If you want to serialize or deserialize your object take a look here:
How to save/restore serializable object to/from file?
You cant really compare ToString() with a "Explicit cast". Both are different indeed.
Plausible comparison should be like this. You should be trying to cast "MyClass to string", that would fail.
Neither Cast from MyClass to string nor string to MyClass` is allowed.*[1]
var myClass = new MyClass() {Id=1};
string myClassString = (string)myClass;//Note this also will fails since no conversion beween `MyClass` to `string`
When you compare ToString method ideally you should be comparing with FromString method unfortunately no such thing.
Back to your question
var myClassConverted = (MyClass)myClassString;
WHY I CANNOT DO?:
Because there is no implicit or explicit conversion between string to MyClass.
[1]To make it work you may use implicit or explicit operators though.

How to pass objects into an attribute constructor

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
{
}

Restricting T to string and int?

I have built myself a generic collection class which is defined like this.
public class StatisticItemHits<T>{...}
This class can be used with int and string values only. However this
public class StatisticItemHits<T> where T : string, int {...}
won't compile. What am I doing wrong?
The type restriction is meant to be used with Interfaces. Your sample suggests that you want to allow classes that inherit from int and string, which is kinda nonsense. I suggest you design an interface that contains the methods you'll be using in your generic class StatisticItemHits, and use that interface as restriction. However I don't really see your requirements here, maybe you could post some more details about your scenario?
You could make StatisticItemHits<T> an abstract class and create two subclasses:
StatisticItemHitsInt : StatisticItemHits<int>{}
StatisticItemHitsString : StatisticItemHits<string>{}
That way there can only be an int and string-representation of StatisticItemHits
As others have said, you cannot use type restrictions in this way. What you can do is to specialise from your base type as follows:
public class StatisticItemHits <T> { }
public class StringStatisticItemHits : StatisticItemHits<string> { }
public class IntegerStatisticItemHits : StatisticItemHits<int> { }
Also, as your base class is generic, you won't be able to use this to polymorphically. If you need to do this make StatisticItemHits implement an interface and use this to reference instances. Something like:
public class StatisticItemHits <T> : IStaticticItemHits { }
public interface IStatisticItemHits { }
You cannot restrict it to string and int from the where clause. You can check it in the constructor, but that is probably not a good place to be checking. My approach would be to specialize the class and abstract the class creation into a (semi-)factory pattern:
class MyRestrictedGeneric<T>
{
protected MyRestrictedGeneric() { }
// Create the right class depending on type T
public static MyRestrictedGeneric<T> Create<T>()
{
if (typeof(T) == typeof(string))
return new StringImpl() as MyRestrictedGeneric<T>;
if (typeof(T) == typeof(int))
return new IntImpl() as MyRestrictedGeneric<T>;
throw new InvalidOperationException("Type not supported");
}
// The specialized implementation are protected away
protected class StringImpl : MyRestrictedGeneric<string> { }
protected class IntImpl : MyRestrictedGeneric<int> { }
}
This way you can limit the class's usage to just string and int internally inside your class.
Given that you only have two types here I would go down an OO route here instead and just have two classes for the two types.
Generics are best used where the circumstances in which they can be applied are, you know, generic. They're a lot less use in circumstances like this.
You can restrict to struct or class types only, and I do think that there need to be numeric or operator based restrictions (e.g. must support +=)
Int and string are really quite different, certainly more different than int and double. It wouldn't make much sense for a generic class to support the immutable reference type of string and the value type of int without also supporting other types more similar to either of them.
Not possible to do this with 'where'. You can't do an 'or'.
Simply use:
where TSource : IEquatable<string>
Checking this:
https://riptutorial.com/csharp/example/8137/where
hybrid value/reference type
Occasionally it is desired to restrict type arguments to those
available in a database, and these will usually map to value types and
strings. As all type restrictions must be met, it is not possible to
specify where T : struct or string (this is not valid syntax). A
workaround is to restrict type arguments to IConvertible which has
built in types of "... Boolean, SByte, Byte, Int16, UInt16, Int32,
UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char, and
String." ...
public class Cup<T> where T : IConvertible
{
// ...
}
I solved the same problem you have with this solution, see my working example here:
static void Main()
{
var r1 = TryTest<int>(123); //Unmanaged supported
var r2 = TryTest<DateTime>(DateTime.Now); //Unmanaged supported
var r3 = TryTest<double>(123.55); //Unmanaged supported
var r4 = TryTest<string>("Homer Simpson"); //String supported
}
public static List<T> TryTest<T>(T someValue) where T : IConvertible
{
var list = new List<T>(); // Just for the sake of the example, a list of <T>
list.Add(someValue); //Add it...
return list;
}
The result will be a List of each type, string included. (Use watch to confirm)

Categories