How to skip obsolete values when I'm casting my int values to enums? I have minimum working example below:
using System;
public class Program
{
public static void Main()
{
int valueInt = 1;
var en = (TestObsolete)valueInt;
Console.WriteLine(en);
}
}
enum TestObsolete
{
Undefined,
[Obsolete]
ValueA = 1,
ValueB=1,
}
I'm getting ValueA, but expecting to get ValueB. Especially I'm interested in generic method with following signature:
public static T ParseEnumWithoutObsolete<T>(int val) where T: struct {
I tried to do something like that:
T #enum;
var enumValues = Enum.GetValues(typeof(T)).Cast<T>();
var obsoleteValues = enumValues.Where(a => typeof(T).GetField(a.ToString()).CustomAttributes.Any(t => t is ObsoleteAttribute));
var activeValues = enumValues.Except(obsoleteValues);
but stuck in the next step.
I'm getting ValueA, but expecting to get ValueB.
You're getting the value represented by the integer 1. The fact that you're seeing that as ValueA has nothing to do with Parse, and everything to do with ToString.
It's really, really important to bear in mind that when you have a value of an enum type, it's really just an integer. There can be several names for the same integer value, and they're completely indistinguishable when you've just got the value.
It sounds to me like what you really want to do - possibly in addition to your parsing method - is write a ToStringWithoutObsolete method. That could map values to names, but only for values which don't have the obsolete attribute. Note that using typeof(T).GetField(a.ToString()) anywhere in your code will make the results unpredictable. It would be better to get all the static fields within the enum type. So for example:
var valueToName = typeof(T)
.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)
.Where(f => !f.IsDefined(typeof(ObsoleteAttribute), false))
.ToDictionary(f => (T) f.GetValue(null),
f => f.Name);
Related
I'm trying to make sense of the code below, can someone please explain to me (in simple terms) how and what gets converted. In particular this part is confusing me (this IEnumerable> values)
Code:
public static class ConvertExtensions
{
public static IEnumerable<TTarget> ConvertAll<TSource, TTarget>(this IEnumerable<IConvertModel<TSource, TTarget>> values) => values.Select(value => value.Convert);
}
It's basically, for every value in a collection, call the T.Convert function, (where T is the target type) and return another collection of the converted values.
It doesn't DO any conversion, it hands the conversion off to a different function depending on the output type.
It's a shorthand way of doing something like this:
var convertedValues = new List<TTarget>();
foreach(var value in values)
{
var convertedValue = value.Convert();
convertedValues.Add(convertedValue);
}
return convertedValues;
Foo and Bar enum members share the same associated value of 180:
public enum EnumShareValues
{
Default = 0,
Foo = 180,
Bar = 180,
}
This code prints String representation of 'Bar' is 'Foo' which is correct, it returns the first occurence of value 180:
const EnumShareValues bar = EnumShareValues.Bar;
Debug.WriteLine("String representation of 'Bar' is '{0}'", bar);
But is it somehow possible to get both names for enum item with value of 180?
Something like:
// would in this case return "Foo, Bar"
Enum.GetAllNames(typeof(EnumShareValues), EnumShareValues.Bar, ",");
Edit:
Finally after some testing and fiddling I ended with the following code. Thank you for all your answers!
public static IEnumerable<string> GetAllNames<T>(T propValue)
where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T must be an enumerated type");
var allNames = Enum.GetNames(typeof(T))
.Where(name => ((T)Enum.Parse(typeof(T), name)).Equals(propValue));
return allNames;
}
Your enums need to be unique, if you duplicate the id then you end up with the following stored internally - look for the debug window with the values in the collection. I suggest a small refactor of the code to accommodate.
this general-puprose method works (fiddle):
i start from enum names and get corresponding values:
public static class Util
{
public static IEnumerable<string> GetSynonims(this Enum value)
{
Type e = value.GetType();
return Enum.GetNames(e).Where(n => Enum.Parse(e, n).Equals(value));
}
}
usage:
Console.WriteLine(String.Join(", ", EnumShareValues.Bar.GetSynonims()));
prints:
Foo, Bar
There's nothing built in, but you can string a few different methods together easily enough:
var allNames =
Enum.GetNames(typeof (EnumShareValues))
.Where(name => (EnumShareValues)Enum.Parse(typeof (EnumShareValues), name)
== EnumShareValues.Bar);
If we were allowed to restrict generic methods to enums, this would be great to wrap up as a generic helper method. You can, of course, do so, but it will produce runtime errors if you try to use it with a non-enum based type.
I have a struct that has a ton of named bool members. (Please leave good practices and such out of consideration now for the sake of the example).
I want to randomly set these values to either true or false, just for testing purposes.
How can I do this through reflection?
This is what I have so far:
Random r = new Random();
foreach (var bool_field in state.GetType().GetFields().Where(x => x.GetType() == false.GetType()))
{
bool_field.SetValue(state, r.Next() % 2 == 0 ? true : false);
}
Unfortunately, it never enters the loop.
UPDATE 1:
Here is how the SystemState struct looks like, which is the type of the state variable. It is filled with bool auto properties.
public struct SystemState
{
...
public bool CanYouHelpMeOnThisOne { get; set; }
...
}
UPDATE 2:
The solution posted below seems nice, but it does not work. The reason is that SystemState is a struct not a class. Therefor it is a Value type not a Reference type, so calling the SetValue method has no effect. After changing SystemState to class, the below solution works perfectly.
Now is there any other way, to achieve this without changing SystemState to class?
At first you need to change the Where clause:
x => x.FieldType == false.GetType()
As the type of x is FieldInfo
Since the last OP's edit it's not fields there, but properties. So, you should use GetProperties to get the list of properties, and compare the PropertyType, as x would be PropertyInfo then. Also the SetValue method is gonna to have the other signature.
foreach (var bool_field in state.GetType()
.GetProperties()
.Where(x => x.PropertyType == false.GetType()))
{
bool_field.SetValue(state, r.Next() % 2 == 0 ? true : false, null);
}
Based on OP's comments, if you do want to access the backing fields, you can use the following code:
var fs = state.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
.Where(x => x.FieldType == typeof(bool));
foreach (var f in fs)
{
f.SetValue(state, true);
}
The overload of GetFields without arguments returns all the public fields of the current Type. While in your case you need the private ones, so use the Type.GetFields Method (BindingFlags) with the suitable BindingFlags.
Don’t know why I can’t figure this out…
I am using a comparison method that takes two types, loops through them, and using reflection builds a report of differences of the fields. This is more or less a detailed comparison of the two types. The types have the same properties. While I am doing the comparison, each of the fields also has a custom attribute associated with it. In the below, the Difference object stores the names, and other properties related to the comparison operation.
To do the comparison, I use the following:
public static List<Difference<T>> Detailed Difference <T>(this T val1, T val2)
{
List< Difference <T>> differences = new List< Difference <T>>();
FieldInfo[] fi = val1.GetType().GetFields();
foreach (FieldInfo f in fi)
{
Difference <T> v = new Difference <T>();
v.Prop = f.Name;
v.ValA = f.GetValue(val1);
v.ValB = f.GetValue(val2);
v.ObjectA = val1;
v.ObjectB = val2;
v.MyCustomAttribute = (MyCustomAttribute Attribute) Attribute.GetCustomAttribute(f, typeof (MyCustomAttribute Attribute));
differences.Add(v)
}
return differences;
}
MyCustomAttribute is an enum that contains FooA and FooB.
If MyCustomAttribute equals FooA, the value of ValA should be used to build a return of type T to add to the method List return. If MyCustomAttribute equals FooB, the value of ValB should be used when building the new type. The problem I am having is the generic doesn’t allow me to instantiate a new type of T, for pretty obvious reasons...so I can’t figure out how to, more or less, map the property values based on reading the custom attribute.
If you include the new constraint, then new T() will be possible. Then you can use FieldInfo.SetValue to set the values in it.
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2)
where T : new()
{
T newValue = new T();
// later...
v.MyCustomAttribute = // whatever it is
if (v.MyCustomAttribute == MyCustomAttribute.FooA)
f.SetValue(newValue, v.ValA);
else if (v.MyCustomAttribute == MyCustomAttribute.FooB)
f.SetValue(newValue, v.ValB);
differences.Add(v);
// other stuff...
}
If this won't work with your types, you'll need something passed in to your method. Since you apparently only need one new object, and always one object, you could just take another T:
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2,
T newValue)
{
// call like
var diff = myClass1.Difference(myClass2, new MyClass(someParam));
For more advanced scenarios, take a Func<T>, so that you can call it whenever needed:
public static List<Difference<T>> Detailed Difference<T>(this T val1, T val2,
Func<T> getNewValue)
{
T newValue = getNewValue();
// call like
var diff = myClass1.Difference(myClass2, () => new MyClass(someParam));
I'm trying to call a function in a dynamic linq select statement, but im getting error:
No property or field 'A' exists in type 'Tuple2'
Example code:
void Main()
{
var a = new Tuple<int, int>(1,1);
var b = new[]{ a };
var q = b.AsQueryable().Select("A.Test(it.Item1)");
q.Dump();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
How should I change my code to get this working?
If I call built in function Convert.ToInt32 for example it works fine.
var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");
Also how do I cast a property using dynamic linq?
var q = b.AsQueryable().Select("((float)it.Item1)");
I'll say that the dynamic-linq isn't "strong enough" to do these things. It looks for methods only in the given objects and some special classes: Math, Convert, the various base types (int, float, string, ...), Guid, Timespan, DateTime
The list of these types is clearly visible if you use ilspy/reflector on the file. They are in System.Linq.Dynamic.ExpressionParser.predefinedTypes .
Now, clearly I could be wrong, but this works: .Select("Guid.NewGuid().ToString()").Cast<string>().ToArray()
showing that it's quite probable that that is the "correct" list.
There is an article here on how to modify Dynamic LINQ if you are interested http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html
Now, an intelligent man would take the source of dynamic linq and simply expand that array... But here there aren't intelligent men... There are only programmers that want blood! Blood but especially innards!
var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");
FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);
Type[] predefinedTypes = (Type[])field.GetValue(null);
Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type
field.SetValue(null, predefinedTypes);
Do this (with the types you want) BEFORE the first call to Dynamic Linq (because after the first call the methods/properties of these types are cached)
Explanation: we use reflection to add our object(s) to this "special list".
I know there is already an accepted answer on this but it did not work for me. I am using Dynamic Linq 1.1.4. I wanted to do a query like this
$.GetNewestRisk() == null
Where GetNewestRisk() is a public method on the object represented by $. I kept getting this error "Error running query, Methods on type 'Patient' are not accessible (at index 2)".
I found in the source code there is a GlobalConfig object that allows a custom provider to be assigned which will hold all of the types you may want to work with. Here is the source code for the custom provider:
public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
public HashSet<Type> GetCustomTypes()
{
HashSet<Type> types = new HashSet<Type>();
types.Add(typeof(Patient));
types.Add(typeof(RiskFactorResult));
types.Add(typeof(PatientLabResult));
types.Add(typeof(PatientVital));
return types;
}
}
Here is how I am using it:
System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();
After making this call I am able to call methods on the objects inside of the expression.
#xanatos answer doesn't work for .Net Core version. So I've found something similar related by #Kent on the System.Dynamic.Linq.Core tests DynamicExpressionParserTests written by the library's author himself.
The given TestCustomTypeProviderClass allows you to use the DynamicLinqType class annotation which is pretty usefull for this problem.
To answer to question, you then just needed to defined the class (ensure to annotate with DynamicLinqType) :
[DynamicLinqType]
public static class A
{
public static int Test(int i)
{
return i++;
}
}
Add a customTypeProvider as mentioned above :
private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
private HashSet<Type> _customTypes;
public virtual HashSet<Type> GetCustomTypes()
{
if (_customTypes != null)
{
return _customTypes;
}
_customTypes = new HashSet<Type>(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly }));
return _customTypes;
}
}
and use a ParsingConfig with the configurable Select to call it :
var config = new ParsingConfig
{
CustomTypeProvider = new TestCustomTypeProvider()
};
var q = b.AsQueryable().Select(config, "A.Test(it.Item1)");
#Armand has put together a brilliant solution for this issue, and being the only solution I was able to find regarding this I want to add to it for anyone who tries the same approach.
The class that is marked with...
[DynamicLinqType]
... must be taken into consideration when you run the following line:
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })
In the solution provided above, this assumes the class that contains the function to be evaluated is on the same class the code currently resides in. If the methods are to be used outside of said class, the assembly will need to change.
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { typeof(AnotherClassName).Assembly })
Nothing changes from the solution above, this is just for clarification for anyone attempting to use it.
As regards the current version (1.2.19) of Dynamic LINQ, you will probably get another exception:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum value 'Test' is not defined in enum type 'A'
To make DLINQ know your type 'A', you have two options:
Set up parsing config with your own custom types provider where you directly specify the type 'A'.
Mark your type with the attribute [DynamicLinqType]. If that type is loaded into the current domain (that's the usual case), you don't have to do anything more since the default custom type provider already scans the current AppDomain for types marked with [DynamicLinqType]. And only if that's not the case, i.e. your type is not loaded into the current domain, you have to do something like in that answer.
What if you would like to use both approaches - the first for type 'A' and the second for type 'B'? In that case, you just have to "merge" your type 'A' with the default provider types:
public class DynamicLinqTests
{
[Test]
public void Test()
{
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var parsingConfig = new ParsingConfig
{
ResolveTypesBySimpleName = true,
CustomTypeProvider = new TestCustomTypesProvider()
};
var queryWithA = b.AsQueryable().Select(parsingConfig, "A.Test(it.Item1)");
queryWithA.ToDynamicList();
var queryWithB = b.AsQueryable().Select(parsingConfig, "B.Test(it.Item1)");
queryWithB.ToDynamicList();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
[DynamicLinqType]
public static class B
{
public static int Test(int i)
{
return i++;
}
}
public class TestCustomTypesProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var customTypes = base.GetCustomTypes();
customTypes.Add(typeof(A));
return customTypes;
}
}
}
I may be confused but your syntax whereby you are using a string in your Selects doesn't compile for me. The following syntax works:
var q = b.AsQueryable().Select(it => A.Test(it.Item1));
var b = new[]{ a };
The above array is don't know what type of array , and it's not type safe ?
Your values are assigned in variant data type so it's not integer value (I think string value) ,when you get this values in your query must need to convert.toint32() because your class parameter data type is integer
Please try it
var b = new **int**[]{ a };
instead of var b = new[]{ a };
The important hint is here (in bold):
No property or field 'xxx' exists in **type** 'xxx'
And Please look this for previous discussion :
Dynamic Linq - no property or field exists in type 'datarow'
The following works for me:
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var q = b.AsQueryable().Select(it=>A.Test(it.Item1));
var q1 = b.AsQueryable().Select(it => Convert.ToInt32(it.Item1));
var q2 = b.AsQueryable().Select(it => (float) it.Item1);