Check parameter range on generic types - c#

Is there a way to do something like "string.Compare()", but for generic types. I want to check the range of some property values.
Here is what I am doing as a work around, but it is pretty ugly:
public class SomeClass<T>
{
public T MinValue { get; set; }
public T MaxValue { get; set; }
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
// Restrict range to Min/Max
if (Comparer<T>.Default.Compare(MaxValue, value) < 0)
_value = MaxValue;
if (Comparer<T>.Default.Compare(MinValue, value) > 0)
_value = MinValue;
}
}
}

This code demonstates what I was talking about in my comment. Of course you will have to modify it to fit with your precise paradigm, of using it in a comparer, but this should be clear enough...
public class Program
{
public static void Main(string[] args)
{
System.Console.WriteLine("Hello World!");
TestObject testObject = new TestObject(15);
TestObject testObject2 = new TestObject(9);
TestObject testObject3 = new TestObject(31);
System.Console.ReadLine();
}
}
public class TestObject
{
[ValidateIntMin(Min = 10)]
[ValidateIntMax(30)]
public int SomeInt { get; set; }
public TestObject(int value)
{
SomeInt = value;
if (!Validator.Validate(this))
{
System.Console.WriteLine("Invalid Value assigned: " + value);
}
else
{
System.Console.WriteLine("" + SomeInt + " was a valid value");
}
}
}
public class ValidateIntMax : Attribute
{
public int Max { get; set; }
public ValidateIntMax(int MaxValue)
{
Max = MaxValue;
}
}
public class ValidateIntMin: Attribute
{
public int Min { get; set; }
}
public static class Validator
{
public static bool Validate<T>(T input) {
var attrType = typeof(T);
var properties = attrType.GetProperties();
bool isValid = true;
foreach (PropertyInfo propertyInfo in properties)
{
var customerMaxValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMax), false).FirstOrDefault();
var customerMinValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMin), false).FirstOrDefault();
if (customerMaxValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMax)customerMaxValueInt).Max;
if (currentPropertyInfoBeingTested > currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
if (customerMinValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMin)customerMinValueInt).Min;
if (currentPropertyInfoBeingTested < currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
}
return isValid;
}
}
Should give the output:
Hello World!
15 was a valid value
Invalid Value assigned: 9
Invalid Value assigned: 31
Of course you can add validation for different types, etc.
This is just to show a totally custom way of setting up your attributes.
I recommend you read up on the ValidationAttribute however, to see if you can't use the implemented functionality.
But this is just a PoC piece.

Related

c# reflection comparing 2 objects with boolean properties

I have two objects with several properties that are of type boolean.
The properties are identical in terms of naming and I just want to make sure they are equal. I do a lot of these checks and want to know if anyone has any suggestion of a method that can do the following code in one step but is dynamic enough to check the property options by name if they have the same name?
if (options != null && other != null)
return options.Quantities == other.Quantities &&
options.SKUEntityKey == other.SKUEntityKey &&
options.LineItemType_Type == other.LineItemType_Type &&
options.SKUIdentifier == other.SKUIdentifier &&
options.Identifier == other.Identifier;
If what I'm asking isn't clear please let me know
class Program
{
static void Main(string[] args)
{
ReflectOverProperties<TestClass, TestClass2>(new TestClass(), new TestClass2());
Console.ReadLine();
}
public static void ReflectOverProperties<T, Z>(T x, Z y)
{
var properties = typeof(T).GetProperties();
foreach (var item in properties)
{
CompareProperty(x, y, item.Name);
}
}
private static void CompareProperty<T, Z>(T x, Z y, string itemName)
{
dynamic originalValue = GetPropValue(x, itemName);
dynamic newValue = GetPropValue(y, itemName);
PropertyCompare(itemName, originalValue, newValue);
}
private static void PropertyCompare(string itemName, dynamic originalValue, dynamic newValue)
{
if (originalValue != newValue)
{
Console.Write($"Property {itemName} does not match");
}
}
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
}
public class TestClass
{
public TestClass()
{
Test1 = false;
Test2 = false;
Test3 = false;
}
public bool Test1 { get; set; }
public bool Test2 { get; set; }
public bool Test3 { get; set; }
}
public class TestClass2
{
public TestClass2()
{
Test1 = false;
Test2 = false;
Test3 = true;
}
public bool Test1 { get; set; }
public bool Test2 { get; set; }
public bool Test3 { get; set; }
}
Here is some Code I modified pretty quick, which runs in console app. Hopefully set you in the right direction, was doing the same object now can be different. Just using some basic reflection and dynamic properties.
I hope this terrible console app will at least give an idea of how to go about doing what you want:
static void Main(string[] args)
{
Person steve = new Person()
{
IsHungry = true,
IsLazy = false,
IsSleepy = true
};
Dog bella= new Dog()
{
IsHungry = true,
IsLazy = false,
IsSleepy = true
};
bool match = DoAllBoolPropertiesMatch(steve, bella);
Console.WriteLine($"\r\n----> Do Bools in Objects Match?: {match}");
}
private static bool DoAllBoolPropertiesMatch(object obj1, object obj2)
{
// For each Boolean property of object 1, check object 2:
foreach(PropertyInfo propInfo in obj1.GetType().GetProperties())
{
// Property is boolean.
if(propInfo.PropertyType == typeof(Boolean))
{
// Look for a property on obj2 with the same name that also returns a bool.
PropertyInfo matchingPropInfo = obj2.GetType().GetProperty(propInfo.Name, typeof(Boolean));
if(matchingPropInfo != null)
{
Console.WriteLine($"Evaluating Property {propInfo.Name} from obj1:");
Console.WriteLine($" - Value for Obj1 = [{propInfo.GetValue(obj1)}]");
Console.WriteLine($" - Value for Obj2 = [{matchingPropInfo.GetValue(obj2)}]");
if(Convert.ToBoolean(propInfo.GetValue(obj1)) != Convert.ToBoolean(matchingPropInfo.GetValue(obj2)))
return false;
}
}
}
return true;
}
public class Person
{
public bool IsHungry { get; set; }
public bool IsSleepy { get; set; }
public bool IsLazy { get; set; }
}
public class Dog
{
public bool IsHungry { get; set; }
public bool IsSleepy { get; set; }
public bool IsLazy { get; set; }
}

How to auto-ignore all properties with no corresponding constructor parameter when serializing

Is there some non-attribute-based method of ignoring all properties that don't have a corresponding constructor parameter when serializing? For example, when serializing this class, property Combo should be ignored. A round-trip serialization/deserialization of an instance of MyClass doesn't require Combo to be serialized. Ideally I could use some out-of-the-box setting.
public class MyClass
{
public MyClass(int myInt, string myString)
{
this.MyInt = myInt;
this.MyString = myString;
}
public int MyInt { get; }
public string MyString { get; }
public string Combo => this.MyInt + this.MyString;
}
You can do this with a custom IContractResolver:
public class ConstructorPropertiesOnlyContractResolver : DefaultContractResolver
{
readonly bool serializeAllWritableProperties;
public ConstructorPropertiesOnlyContractResolver(bool serializeAllWritableProperties)
: base()
{
this.serializeAllWritableProperties = serializeAllWritableProperties;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
if (contract.CreatorParameters.Count > 0)
{
foreach (var property in contract.Properties)
{
if (contract.CreatorParameters.GetClosestMatchProperty(property.PropertyName) == null)
{
if (!serializeAllWritableProperties || !property.Writable)
property.Readable = false;
}
}
}
return contract;
}
}
Then use it like:
var settings = new JsonSerializerSettings { ContractResolver = new ConstructorPropertiesOnlyContractResolver(false) };
var json = JsonConvert.SerializeObject(myClass, Formatting.Indented, settings );
Pass true for serializeAllWritableProperties if you also want to serialize read/write properties that are not included in the constructor parameter list, e.g. AnUnrelatedReadWriteProperty in:
public class MyClass
{
public MyClass(int myInt, string myString)
{
this.MyInt = myInt;
this.MyString = myString;
}
public int MyInt { get; private set; }
public string MyString { get; private set; }
public string Combo { get { return this.MyInt + this.MyString; } }
public string AnUnrelatedReadWriteProperty { get; set; }
}
Note you may want to cache your contract resolver for best performance.

Is it possible to set the property of the condition dynamically?

Normally when I want for example to find the first or default item of a List I use this way:
myList.SingleOrDefault(x=>x.MyPropery01 == "myCondition");
However, I would like to know if it is possible, for example by reflection, if I set the the property MyProperty dynamically, something like:
myList.SingleOrDefault(x=>x.GetType().GetProperty("MyProperty01") == "myCondition");
Because sometimes I need to search for MyProperty01, sometimes for MyProperty02, MyProperty03, etc..
EDIT: in visual studio I get this error:
"Operator '==' can't be applied to operands of type System.reflection.PropertyInfo and string".
Yeah you can do that. You were pretty close, here is a demo you can drop in linqpad. Note that the important part is
Single(l => l.GetType().GetProperty(prop).GetValue(l).ToString() == "Condition")
void Main()
{
var myList = Enumerable.Range(0,10).Select(i => new Xmas(i,"name"+i)).ToList();
string prop = "name";
Console.WriteLine(myList.Single(l => l.GetType().GetProperty(prop).GetValue(l).ToString() == "name6").name);
}
public class Xmas
{
public int id { get; set; }
public string name { get; set; }
public Xmas( int id, string name )
{
this.id = id;
this.name = name;
}
}
Working example:
public class Apple
{
public string Color { get; set; }
}
public List<Apple> ApplesList {get;set;}
public void Process()
{
PropertyInfo pi = typeof(Apple).GetProperty("Color");
ApplesList = ApplesList.Where(r => (string)pi.GetValue(r) == "Red").ToList();
}
You could also write an Extension method, that allow to get the property on every object, returning null when it doesn't exist, or doesn't have a GetMethod. You could keep a Cache if you want
public static class ObjectExtension
{
static IDictionary<KeyValuePair<Type, string>, PropertyInfo> propertyCache = new Dictionary<KeyValuePair<Type, string>, PropertyInfo>();
public static object GetProperty(this object source, string propertyName, bool useCache = true)
{
if (source == null)
{
return null;
}
var sourceType = source.GetType();
KeyValuePair<Type, string> kvp = new KeyValuePair<Type, string>(sourceType, propertyName);
PropertyInfo property = null;
if (!useCache || !propertyCache.ContainsKey(kvp))
{
property = sourceType.GetProperty(propertyName);
if (property == null)
{
return null;
}
var get = property.GetGetMethod();
if (get == null)
{
return null;
}
if (useCache)
{
propertyCache.Add(kvp, property);
}
}
else
{
property = propertyCache[kvp];
}
return property.GetValue(source, null);
}
public static T GetProperty<T>(this object source, string propertyName)
{
object value = GetProperty((object)source, propertyName);
if (value == null)
{
return default(T);
}
return (T)value;
}
}
A small test class could then be:
public class Item
{
public string MyProperty { get; set; }
public string MyProperty3 { get; set; }
public string MyProperty2 { protected get; set; }
public Item()
{
MyProperty = "Test propery";
MyProperty3 = "Test property 3";
MyProperty2 = "Yoohoo";
}
}
With a main class for testing
class Program
{
static void Main(string[] args)
{
Item item = new Item();
for (int x = 0; x < 4; x++)
{
string key = "MyProperty" + (x > 0 ? x.ToString() : "");
string value = item.GetProperty<string>(key);
Console.WriteLine("Getting {0} = {1}", key, value);
}
Console.ReadLine();
}
}
which gives the expectable output of:
Getting MyProperty = Test propery
Getting MyProperty1 =
Getting MyProperty2 =
Getting MyProperty3 = Test property 3

How to discover all static fields of a certain type within a UITypeEditor?

I want people who use my custom control to choose a constant with a simple dropdown. The control should be reusable and therefor resides in a library so it can't access an enum, and the constants could in fact be defined anywhere.
Problem is that AppDomain.CurrentDomain.GetAssemblies() does not contain the current project you are designing in. And when I create a separate project it sometimes works but mostly it doesn't. And with doesn't work I mean: It can't find any classes/constants defined in the library, so they don't show up in the dropdown.
Everything else already works. The dropdown works. Code generation is a but clunky but it works. And I can always find constants defined in TestControl itself.
It seems like the designer itself doesn't always load all the assemblies/projects which are available. So how can I load the project which I need within the context of the designer/visual studio itself? Or how do I reference/find other projects within my list of dependencies from within the UITypeEditor?
I have created my own TestControl:
public partial class TestControl : UserControl, IHasTags
{
public static readonly ITestItem A = new TestItemImpl(null, "A", "Aap");
public static readonly ITestItem B = new TestItemImpl(null, "B", "B");
public static readonly ITestItem C_xx = new TestItemImpl(null, "C", "C");
public static readonly ITestItem D = new TestItemImpl(null, "D", "D");
[Editor(typeof(ItemEditor), typeof(UITypeEditor))]
public ITestItem item { get; set; }
public TestControl()
{
InitializeComponent();
}
}
With my own editor:
public class ItemEditor : UITypeEditor
{
private IWindowsFormsEditorService _editorService;
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// drop down mode (we'll host a listbox in the drop down)
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
_editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
// use a list box
ListBox lb = new ListBox();
lb.SelectionMode = SelectionMode.One;
lb.SelectedValueChanged += OnListBoxSelectedValueChanged;
lb.Width = 300;
// get the analytic object from context
// this is how we get the list of possible benchmarks
TestControl ctrl = (TestControl)context.Instance;
// Add all test items
IList<StaticFieldInfo<ITestItem>> items = ClassHelper.getStaticFields<IHasTags, ITestItem>();
foreach (StaticFieldInfo<ITestItem> item in items)
{
lb.Items.Add(item);
}
// show this model stuff
_editorService.DropDownControl(lb);
if (lb.SelectedItem == null) // no selection, return the passed-in value as is
return value;
StaticFieldInfo<ITestItem> result = (StaticFieldInfo<ITestItem>)lb.SelectedItem;
return result.value;
}
private void OnListBoxSelectedValueChanged(object sender, EventArgs e)
{
// close the drop down as soon as something is clicked
_editorService.CloseDropDown();
}
}
And I collect all constants with the following helper:
public class StaticFieldInfo<T>
{
public StaticFieldInfo(FieldInfo fieldInfo, T value)
{
this.fieldInfo = fieldInfo;
this.value = value;
}
public T value { get; private set; }
public FieldInfo fieldInfo { get; private set; }
public override string ToString()
{
return value.ToString() + " (" + fieldInfo.DeclaringType.FullName + "." + fieldInfo.Name + ")";
}
}
public class ClassHelper
{
public static IList<StaticFieldInfo<T>> getStaticFields<C, T>()
{
Type classType = typeof(C);
Type fieldType = typeof(T);
List<StaticFieldInfo<T>> result = new List<StaticFieldInfo<T>>();
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
foreach (Type t in asm.GetTypes())
{
if (classType.IsAssignableFrom(t))
{
foreach (FieldInfo f in t.GetFields())
{
if (f.IsStatic && fieldType.IsAssignableFrom(f.FieldType))
{
T value = (T)f.GetValue(null);
if (value != null)
{
result.Add(new StaticFieldInfo<T>(f, value));
}
}
}
}
}
}
catch (ReflectionTypeLoadException)
{
// Ignore errors
}
}
return result;
}
}
The rest of the code (less important):
public interface IHasTags
{
}
[TypeConverter(typeof(ITestItemConverter))]
public interface ITestItem
{
string driver { get; set; }
string tag { get; set; }
string name { get; set; }
}
public class ITestItemConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
// we only know how to convert from to a string
return typeof(string) == destinationType;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value != null)
{
return value.ToString();
}
return "(none)";
}
}
[DesignerSerializer(typeof(ItemSerializer), typeof(CodeDomSerializer))]
public class TestItemImpl : ITestItem
{
public TestItemImpl()
{
}
public TestItemImpl(string driver, string tag, string name)
{
this.driver = driver;
this.tag = tag;
this.name = name;
}
public override string ToString()
{
return name;
}
public string driver { get; set; }
public string tag { get; set; }
public string name { get; set; }
}
public class ItemSerializer : CodeDomSerializer
{
public override object Deserialize(IDesignerSerializationManager manager, object codeObject)
{
// This is how we associate the component with the serializer.
CodeDomSerializer baseClassSerializer = (CodeDomSerializer)manager.
GetSerializer(typeof(TestItemImpl).BaseType, typeof(CodeDomSerializer));
/* This is the simplest case, in which the class just calls the base class
to do the work. */
return baseClassSerializer.Deserialize(manager, codeObject);
}
public override object Serialize(IDesignerSerializationManager manager, object value)
{
/* Associate the component with the serializer in the same manner as with
Deserialize */
CodeDomSerializer baseClassSerializer = (CodeDomSerializer)manager.
GetSerializer(typeof(TestItemImpl).BaseType, typeof(CodeDomSerializer));
object codeObject = baseClassSerializer.Serialize(manager, value);
/* Anything could be in the codeObject. This sample operates on a
CodeStatementCollection. */
if (codeObject is CodeStatementCollection)
{
CodeStatementCollection statements = (CodeStatementCollection)codeObject;
StaticFieldInfo<ITestItem> field = null;
foreach (StaticFieldInfo<ITestItem> item in ClassHelper.getStaticFields<IHasTags, ITestItem>())
{
if (value == item.value)
{
field = item;
break;
}
}
// If field can be found (should be always)
if (field != null)
{
statements = new CodeStatementCollection();
statements.Add(new CodeSnippetExpression(typeof(ITestItem).FullName + " " + this.GetUniqueName(manager, value) + " = " + typeof(TestControl).FullName + "." + field.fieldInfo.Name));
codeObject = statements;
}
}
return codeObject;
}
}

Write a MVC custom validation for specific values

I'm writing a MVC custom validation. It will be valid for list of specific values. Example:
[Values(30, 60, 120)]
public int SelectTop { get; set; }
But It doesn't work with my validation. This is codes:
public class ValuesAttribute : ValidationAttribute
{
public object[] Values { get; private set; }
public Type Type { get; private set; }
public ValuesAttribute(params int[] values)
: this(typeof(int), values)
{
}
public ValuesAttribute(params double[] values)
: this(typeof(double), values)
{
}
public ValuesAttribute(Type type, params object[] values)
{
this.Type = type;
this.Values = values;
}
public override bool IsValid(object value)
{
foreach (var v in this.Values)
{
if (object.Equals(v, value))
{
return true;
}
}
return false;
}
}
Please help me to find the problem. Thanks.
This line
public object[] Values { get; private set; }
stores the array of values in it so Values[0] = int[3]
Change your code to:
public override bool IsValid(object value) {
int[] valueSet = this.Values[0] as int[];
if (valueSet == null) {
throw new Exception("Values must be provided");
}
foreach (var v in valueSet) {
if (object.Equals(v, value)) {
return true;
}
}
return false;
}

Categories