0

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

Kixoka
  • 989
  • 4
  • 15
  • 37
  • Could you pass to the method `MyCheckMethod` directly the entire object `obj`? – Dave Mar 08 '19 at 14:28
  • I think it would be clearer to validate the value inside the property, if your `Prop1` is non-null and non-empty according to the contract. – Yeldar Kurmangaliyev Mar 08 '19 at 14:29
  • @YeldarKurmangaliyev - the problem is that I need to only update the property if the value coming in valid and if needed then convert to the appropriate data type. The property needs to stay null until there is a valid value. Plus i have over 100 properties so I am trying to eliminated any bloated coding. – Kixoka Mar 08 '19 at 14:38
  • Where are the values coming from? There's a good chance that there's already a tool for what you need to do. – Scott Hannen Mar 08 '19 at 14:53
  • What determines whether a value is valid, and what is the type of the incoming data - is it an object, string, or something else? One way to approach this is if you describe the expected use in more detail. I think a number of answers (mine included) are focused on literally answering your question, but I bet there's a better approach. – Scott Hannen Mar 08 '19 at 15:00
  • @ScottHannen - sounds good... I have updated the post. The incoming data is always as a string. – Kixoka Mar 08 '19 at 15:06
  • What is the file format in which the data is supplied? There are libraries for CSV, JSON, Excel, and more. Do you want to throw an exception if the data is invalid? – Scott Hannen Mar 08 '19 at 15:12
  • @ScottHannen - the data is supplied via API call for the 3rd party product... part of their SDK. My final output is in JSON. If the data is invalid thats ok... thats why the property should default to a NULL value. The requirements at this time do not want any errors thrown. – Kixoka Mar 08 '19 at 15:17
  • Perhaps something like this is what you want. Create models for deserializing the JSON and ignore deserialization errors. https://stackoverflow.com/questions/26107656/ignore-parsing-errors-during-json-net-data-parsing – Scott Hannen Mar 08 '19 at 16:03

4 Answers4

2

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);
        }
    }
MistyK
  • 6,055
  • 2
  • 42
  • 76
  • This code won't allow you to assign a `string` to a property of type `object`, for example. – Sean Mar 08 '19 at 15:05
  • 1
    Yes, it does assign a string to a property of type object. Just tested it. – Isma Mar 08 '19 at 15:11
  • Your example looks to be missing a parameter on the method/usage call.... – Kixoka Mar 08 '19 at 15:34
  • 1
    @MistyK This only works if the data source is the same as the data type of the property... please see my updated info. The incoming data will always be string... the property data type could be anything.... – Kixoka Mar 08 '19 at 15:52
  • Write a logic, if it's DateTime then convert DateTime.Parse etc and for other types – MistyK Mar 08 '19 at 15:54
  • @MistyK - thats part of the problem I cannot convert the data until I know that the string value has data in it. Then I can convert (DateTime.Parse...etc) I will update the post with more information. – Kixoka Mar 08 '19 at 16:03
  • Updated the answer – MistyK Mar 08 '19 at 16:15
  • @MistyK - Very nice! Nice learning opportunity for me. Very much appreciate the help!! – Kixoka Mar 08 '19 at 16:23
1

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);
andyb952
  • 1,931
  • 11
  • 25
1

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);
Sean
  • 60,939
  • 11
  • 97
  • 136
  • There is, reflection. – MistyK Mar 08 '19 at 14:39
  • @MistyK - that's not passing a reference to a property, that's passing a `PropertyInfo` instance around. This will be slow, error prone and not type safe and will mean what were once compile time errors will become runtime errors. – Sean Mar 08 '19 at 14:42
  • Slower? Yes but not noticeably unless you call it thousands of times per second. Error prone? Maybe a little. Type safe? See my answer. – MistyK Mar 08 '19 at 14:51
-1

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.