I have a User object with a bunch of properties. I have a requirement that states when a user sets up their information, they need the ability to state which properties of their profile are visible to others.
The way I had envisioned this was adding an additional property - a list of string that would contain the property names that were publicly visible. I could then implement a method called ToPublicView() or something similar that would use reflection to set non-public properties to null or default.
Is this a reasonable approach, or is there a better way?
I think it's the simplest of the options. If reflection start to kill your performance, you may want to have a dictionary of property-delegate for accessing the values.
And as the requirement is not to have dynamic properties but just to mark the existing ones, it doesn't make sense to have all the properties in a dynamic way (like a list of property objects). Also, having them as actual properties will make the code more readable when you have to use it for the rest of the application.
In such a situation, if possible, I would suggest simply having a list of your properties, such as:
public Class Property<T>
{
public Property(string name, bool visible, T value)
{
Name = name;
Visible = visible;
Value = value;
}
string Name { get; set; }
bool Visible { get; set; }
T Value { get; set; }
}
Then you could create a list of the properties like this:
List<Property> properties = new List<Property>();
properties.Add(new Property<string>("FirstName", true, "Steve"));
If you need to be able to set the visibility today, you may need to set other meta-properties as well tomorrow. Color? Required/Optional? Size? Etc. Having your own Property type allows you to easily expand it in the future.
What? No. If that's the demand, then User properties should not be realized with actual properties, but instead using some sort of IEnumerable and Property objects, where each Property has its visibility, etc.
Can use, may be, some combination of custom DynamicObject implementation
EDIT
//custom property class
public class MyProperty
{
public bool IsVisible { get; set; }
public string PropertyName { get; set; }
}
public class Dymo: DynamicObject
{
Dictionary<MyProperty, object> dictionary
= new Dictionary<MyProperty, object>();
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
result = false;
var prop = PropertyFromName(binder.Name);
if (prop != null && prop.IsVisible)
return dictionary.TryGetValue(prop, out result);
return false;
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
var prop = PropertyFromName(binder.Name);
if (prop != null && prop.IsVisible)
dictionary[prop] = value;
else
dictionary[new MyProperty { IsVisible = true, PropertyName = binder.Name}] = value;
return true;
}
private MyProperty PropertyFromName(string name)
{
return (from key in dictionary.Keys where key.PropertyName.Equals(name) select key).SingleOrDefault<MyProperty>();
}
public void SetPropertyVisibility(string propertyName, bool visibility)
{
var prop = PropertyFromName(propertyName);
if (prop != null)
prop.IsVisible = visibility;
}
}
and use this, after like this.
dynamic dynObj = new Dymo();
dynObj.Cartoon= "Mickey" ;
dynObj.SetPropertyVisibility("Mickey", false); //MAKE A PROPERTY "INVISIBLE"
Related
I would like to automatically expand some nodes in a PropertyGrid loaded with an instance of my SettingsStructure class by using attributes on the properties of that class.
Also, I am attempting to have the instance 'remember' whether each property was expanded or not if the user loads that instance again on the PropertyGrid.
I have made a real HACK that mostly works. It does not always work if the complex property is the first one displayed in the PropertyGrid.
Any suggestions for a better way using attributes/typeconverters/similar?
Here is what I have:
Define a custom Attribute to denote that the property it marks is to start expanded.
[AttributeUsage(AttributeTargets.Property)]
public class StartExpanded : Attribute {}
Derive your own class from ExpandableObjectConverter.
public class MyExpandableObjectConverter : ExpandableObjectConverter
{
private bool _IsFirstUse = true;
private bool _JustShownInPropertyGrid = false;
private bool _WasLastExpanded = false;
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
//This method is called every time the propertygrid shows this property
if (_IsFirstUse)
{
_IsFirstUse = false;
_WasLastExpanded = context.PropertyDescriptor.Attributes[typeof(StartExpanded)] != null;
}
_JustShownInPropertyGrid = true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
//This method is called after CanConvertFrom and also on other operations and when leaving showing this property
if (_JustShownInPropertyGrid)
{
_JustShownInPropertyGrid = false;
if (_WasLastExpanded)
{
var GI = (GridItem)context;
GI.Expanded = true;
}
}
else
{
var GI = (GridItem)context;
_WasLastExpanded = GI.Expanded;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
And now apply them to properties in your class to be shown in the PropertyGrid. ASettingsNode is just an abstract class that I use to mark properties that should load in the TreeView on the left.
[Serializable]
public class SettingsStructure : ASettingsNode
{
public string FamilyName { get; set; } = "Rogers";
[StartExpanded]
[TypeConverter(typeof(MyExpandableObjectConverter))]
public NameAgePair Dad { get; set; } = new NameAgePair() { Name = "Buck", Age = 51};
[StartExpanded]
[TypeConverter(typeof(MyExpandableObjectConverter))]
public NameAgePair Mom { get; set; } = new NameAgePair() { Name = "Wilma", Age = 50};
public string NameOfSomebody { get; set; } = "Phoebe";
//... and other nodes that are derived from ASettingsNode to show up in the TreeView
}
Here NameAgePair is my class.
public class NameAgePair
{
public string Name { get; set; } = "";
public int Age { get; set; } = 0;
public override string ToString()
{
return $"{Name} ({Age})";
}
}
Besides being a HACK, it does not work if the first item in the grid is one of the complex properties that I want to expand and remember. For that first property the ConvertTo method is called out of sequence and the "remember" part fails.
There is no direct solution for setting initial expanded states in the MSPG. Only hacks. And, as you can see, trying to do it in a TypeConverter is not easy. Furthermore, there is no built-in way to save and restore various states of the grid.
As the developer of a custom PropertyGrid made from scratch, I tried to achieve both of your requests. And it definitively works with a few lines of code only. If a commercial product is an option, you can have a look.
I am trying to find a way to take a class's property and pass it to a method along with another variable to update the property based on conditions. For example
The class
public class MyClass{
public string? Prop1 { get; set; }
public string? Prop2 { get; set; }
public bool? Prop3 { get; set; }
public DateTime? Prop4 { get; set; }
... etc...
}
Test code I would like to get to work...:
var obj = new MyClass();
MyCheckMethod(ref obj.Prop1, someCollection[0,1]);
in the method:
private void MyCheckMethod(ref Object obj, string value)
{
if (!string.isnullorempty(value))
{
// data conversion may be needed here depending on data type of the property
obj = value;
}
}
I want to be able to pass any property of any class and update the property only after validating the value passed in the method. I was hoping I could do this with generics, but I haven't yet found a way to do so. Or if I am over complicating what I need to do.
The problem is that there may be a bit more to the validation of the passed in value than just a simple isnullorempy check.
I also thought about doing something like this:
private void MyCheckMethod(ref object obj, Action action)
Then I could do something like this:
...
MyCheckMethod(ref obj.Prop1, (somecollection[0,1]) => {
... etc....
})
So I am looking for some guidance on how to proceed.
updated info:
The incoming data is all in string format (this is how a 3rd party vendor supplies the data). The data is supplied via API call for the 3rd party product... part of their SDK. However in my class I need to have proper data types. Convert string values to datetime for dates, string values to int for int data types, etc... . The other caveat is that if there isnt a valid value for the data type then the default value of the property should be NULL.
Additional Information:
The incoming data is always in string format.
eg:
I have to update a boolean property.
The incoming value is "". I test to see if the string Value isNullOrEmpty. It is so I dont do anything to property.
The next property datatype is decimal.
The incoming value is "0.343".
I Test to see if the string value is NullorEmpty. It isnt so I can update the property once I do a convert etc.....
Hope this helps.
Thanks
Full solution after edits:
public static class Extensions
{
//create other overloads
public static void MyCheckMethodDate<TObj>(this TObj obj,Expression<Func<TObj,DateTime>> property, string value)
{
obj.MyCheckMethod(property, value, DateTime.Parse);
}
public static void MyCheckMethod<TObj,TProp>(this TObj obj,Expression<Func<TObj,TProp>> property, string value,Func<string, TProp> converter)
{
if(string.IsNullOrEmpty(value))
return;
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if(null != propertyInfo && propertyInfo.CanWrite)
{
propertyInfo.SetValue(obj, converter(value));
}
}
}
public class Obj
{
public object Prop1{get;set;}
public string Prop2{get;set;}
public DateTime Prop3{get;set;}
}
public class Program
{
public static void Main()
{
var obj = new Obj();
obj.MyCheckMethodDate(x=>x.Prop3, "2018-1-1");
Console.WriteLine(obj.Prop3);
}
}
You can pass a lambda expression:
void DoSomething<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}else{
var name = ((MemberExpression)property.Body).Member.Name;
var value = property.Compile();
//Do whatever you need to do
}
}
To use:
DoSomething(() => obj.Property1);
You can't pass a reference to an arbitrary property. A property is basically implemented as two methods, set_Property and get_Property, and there's no way to bundle these together.
One option is to have your checker function take delegates to access the property. For example:
private void MyCheckMethod(Func<string> getter, Action<string> setter)
{
var value = getter();
var newValue = value.ToUpper();
setter(value);
}
So now you would say something like this:
public class MyClass
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
var c = new MyClass();
MyCheckMethod(() => c.Prop1, value => c.Prop1 = value);
Use reflection with compiled expressions.
This performs better than reflection and a little bit slower than native code.
It's not type safe, but you can add runtime validation.
How can i check that some string is equal to the "constructor" arguments of an attribute?
And how to get all constructor values (TestArg1, TestArg2)?
struct MyData
{
[MyAttr("TestArg1", "TestArg2")] //check that some string equals TestArg1/TestArg2
public string TestArg;
}
This primarily depends on what attribute you're looking at and how it's coded. See the code below as an example on how to do what you're asking.
//The attribute we're looking at
public class MyAtt : System.Attribute
{
public string name;
public string anotherstring;
public MyAtt(string name, string anotherstring)
{
this.name = name;
this.anotherstring = anotherstring;
}
}
public static class Usage
{
[MyAtt("String1", "String2")] //Using the attribute
public static string SomeProperty = "String1";
}
public static class Program
{
public static void Main()
{
Console.WriteLine(IsEqualToAttribute("String1"));
Console.WriteLine(IsEqualToAttribute("blah"));
Console.ReadKey();
}
public static bool IsEqualToAttribute(string mystring)
{
//Let's get all the properties from Usage
PropertyInfo[] props = typeof(Usage).GetProperties();
foreach (var prop in props)
{
//Let's make sure we have the right property
if (prop.Name == "SomeProperty")
{
//Get the attributes from the property
var attrs = prop.GetCustomAttributes();
//Select just the attribute named "MyAtt"
var attr = attrs.SingleOrDefault(x => x.GetType().Name == "MyAtt");
MyAtt myAttribute = attr as MyAtt; //Just casting to the correct type
if (myAttribute.name == mystring) //Compare the strings
return true;
if (myAttribute.anotherstring == mystring) //Compare the strings
return true;
}
}
return false;
}
}
As you can see we get the attribute off the property using reflection and then just compare the properties.
More info can be found here:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/accessing-attributes-by-using-reflection
As far as getting the constructor properties something along the lines of
typeof(MyAtt).GetConstructor().GetParameters()
Would retrieve the parameter details for the constructor.
There is also info on this in the Microsoft Docs: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata.constructor?view=netframework-4.7.2
Here is one way to do what you are asking for, but it's not particularly scaleable and requires a bunch of manual code to get working but may get you on the road to what you are trying to achieve. Assuming we have an attribute something like this that takes a string array in it's constructor:
public class MyAttrAttribute : Attribute
{
public string[] AllowedValues { get; }
public MyAttrAttribute(params string[] values)
{
AllowedValues = values;
}
}
You can change your field to be a property with a backing field. This allows you to override the set method and do your checking in there:
private string _testArg;
[MyAttr("TestArg1", "TestArg2")] //check that some string equals TestArg1/TestArg2
public string TestArg
{
get => _testArg;
set
{
var allowedValues = this.GetType() //Get the type of 'this'
.GetProperty(nameof(TestArg)) // Get this property
.GetCustomAttribute<MyAttrAttribute>() // Get the attribute
.AllowedValues; //Get the allowed values specified in the attribute
if(!allowedValues.Contains(value))
{
throw new ArgumentOutOfRangeException(nameof(value),
$"The value '{value}' is not allowed");
}
_testArg = value;
}
}
Having said all of this, I firmly believe that there is a better way to achieve what you are asking. For example, if you are restricted to a minimal set of values, then an enum would almost certainly be a better option than a string.
I'm trying to find a way to refine some code that I have. I work with a 3rd party API that has a REALLY complicated API request object (I'll call it ScrewyAPIObject) that has tons of repetition in it. Every time you want to set a particular property, it can take a page worth of code. So I built a library to provide a simplified wrapper around the setting/getting of its properties (and to handle some value preprocessing).
Here's a stripped-down view of how it works:
public abstract class LessScrewyWrapper
{
protected ScrewyAPIRequest _screwy = new ScrewyAPIRequest();
public void Set(string value)
{
Set(_getPropertyName(), value);
}
public void Set(string property, string value)
{
// Preprocess value and set the appropriate property on _screwy. This part
// has tons of code, but we'll just say it looks like this:
_screwy.Fields[property] = "[" + value + "]";
}
protected string _getPropertyName()
{
// This method looks at the Environment.StackTrace, finds the correct set_ or
// get_ method call and extracts the property name and returns it.
}
public string Get()
{
// Get the property name being access
string property = _getPropertyName();
// Search _screwy's structure for the value and return it. Again, tons of code,
// so let's just say it looks like this:
return _screwy.Fields[property];
}
public ScrewyAPIRequest GetRequest()
{
return _screwy;
}
}
Then I have a child class that represents one specific type of the screwy API request (there are multiple kinds that all have the same structure but different setups). Let's just say this one has two string properties, PropertyA and PropertyB:
public class SpecificScrewyAPIRequest : LessScrewyWrapper
{
public string PropertyA
{
get { return Get(); }
set { Set(value); }
}
public string PropertyB
{
get { return Get(); }
set { Set(value); }
}
}
Now when I want to go use this library, I can just do:
SpecificScrewyAPIRequest foo = new SpecificScrewyAPIRequest();
foo.PropertyA = "Hello";
foo.PropertyB = "World";
ScrewyAPIRequest request = foo.GetRequest();
This works fine and dandy, but there are different kinds of data types, which involves using generics in my Set/Get methods, and it just makes the child classes look a little kludgy when you're dealing with 50 properties and 50 copies of Get() and Set() calls.
What I'd LIKE to do is simply define fields, like this:
public class SpecificScrewyAPIRequest : LessScrewyWrapper
{
public string PropertyA;
public string PropertyB;
}
It would make the classes look a LOT cleaner. The problem is that I don't know of a way to have .NET make a callback to my custom handlers whenever the values of the fields are accessed and modified.
I've seen someone do something like this in PHP using the __set and __get magic methods (albeit in a way they were not intended to be used), but I haven't found anything similar in C#. Any ideas?
EDIT: I've considered using an indexed approach to my class with an object-type value that is cast to its appropriate type afterwards, but I'd prefer to retain the approach where the property is defined with a specific type.
Maybe in your case DynamicObject is a suitable choice:
public class ScrewyDynamicWrapper : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// get your actual value based on the property name
Console.WriteLine("Get Property: {0}", binder.Name);
result = null;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// set your actual value based on the property name
Console.WriteLine("Set Property: {0} # Value: {2}", binder.Name, value);
return true;
}
}
And define your wrapper objects:
public class ScrewyWrapper
{
protected dynamic ActualWrapper = new ScrewyDynamicWrapper();
public int? PropertyA
{
get { return ActualWrapper.PropertyA; }
set { ActualWrapper.PropertyA = value; }
}
public string PropertyB
{
get { return ActualWrapper.PropertyB; }
set { ActualWrapper.PropertyB = value; }
}
}
However, you can't rely on the property type inside ScrewyDynamicWrapper with this approach, so it depends on your actual API requirements - maybe it won't work for you.
Instead of fields, If you define as property in class, It will be more easy.
public class SpecificScrewyAPIRequest
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
}
Then you can create extension generic method to return ScrewyAPIRequest object.
public static class Extensions
{
public static ScrewyAPIRequest GetRequest<T>(this T obj)
{
ScrewyAPIRequest _screwy = new ScrewyAPIRequest();
var test= obj.GetType().GetProperties();
foreach (var prop in obj.GetType().GetProperties())
{
_screwy.Fields[prop.Name] = prop.GetValue(obj, null);
}
return _screwy;
}
}
Now you can easily get ScrewyAPIRequest from any class object.
Your code will look like following.
SpecificScrewyAPIRequest foo = new SpecificScrewyAPIRequest();
foo.PropertyA = "Hello";
foo.PropertyB = "World";
ScrewyAPIRequest request = foo.GetRequest();
I have a class
class A {
propA { get; set; }
propB { get; set; }
propC { get; set; }
target { get; set; }
}
I work out the target of class A and populate the class with user input.
Each different target will mean different properties of the class are required(not empty/null).
So if my target is banana, then propA and propB must not be empty. If apple, then propB and propC must not be empty. I need to do this at the start of the application as to not keep doing checks in a later stage, as some methods and DB calls will require data etc.
What's the best way to code this? or even design-wise.
Is it good practice to store what properties are required for each target in an enum? And then use what lazyberezovsky provided below to go through and check??
The above example only has 3 properties, but what I'm actually required to do has heaps more.
I only just started looking at ways to validate my code.
In summary, there are two parts to this question.
- How to check whether a property of a class is empty
- Storing a lists of different combined required properties somewhere to use against how to check
EDIT: sorry! I've edited to hopefully make more sense out of this.
Let me try to guess what question was about :)
Here is type validator, which can check existence of parameter's public properties:
public class TypeValidator<T>
{
public bool IsPropertyExists(string propertyName)
{
Type type = typeof(T);
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
foreach (PropertyInfo property in type.GetProperties(flags))
if (property.Name == propertyName)
return true;
return false;
}
}
Usage with your class:
TypeValidator<a> validator = new TypeValidator<a>();
validator.IsPropertyExists("PropB")
Or you can use it as extension method public static bool IsPropertyExists<T>(this T t, string propertyName) with any object or generic parameter. But for me reflection is evil :) Try to resolve this issue by design.
I think what you are after is model validation. If so, perhaps a good place to put your validation logic is in a method of Class A, like this:
Class A
{
Nullable<int> propA { get; set; }
int? propB { get; set; }
int? propC { get; set; }
string target {
get { return _target; }
set {
var oldtarget = _target;
_target = value;
if !IsValid()
{
_target = oldtarget;
throw new Exception("Setting target to "
+ value
+ " is not possible, ....");
}
}
}
public bool IsValid()
{
switch (_target)
{
case "banana":
return propA.HasValue() && propB.HasValue();
case "apple":
return propB.HasValue() && propC.HasValue();
}
return true;
}
}
How to check is propA is empty? Depends on what propA is... is it an int? a bool? a string? etc... assuming it's a int than you can use the method above...
Can't think of a more "generic" validation than IsValid(), that is, I can't think of a more generic way to validate without knowing more...
I guess you mean validation when you say a property exists.
You haven't specified if its an Web or Win Form/Wpf application (or whatelse).
You could implement IDataErrorInfo on you Classes and validate if properties have been filled right.
There are tons of examples to find when Googling on IDataErrorInfo + Validate but here are some which helped me in the past:
http://www.arrangeactassert.com/using-idataerrorinfo-for-validation-in-mvvm-with-silverlight-and-wpf/
How to validate child objects by implementing IDataErrorInfo on parent class