-3

Code:

objectType request = factory.create<objectType>();

public class factory
{
    public static T create<T>() where T : new()
    {
      T obj = new T();
      PropertyInfo propertyInfo = obj.GetType().GetProperty("client_no");
      propertyInfo.SetValue(obj, CLIENT_NUMBER, null);
      return (T)Convert.ChangeType(obj, typeof(T));
     }
}

Explanation:

I am creating a generic factory fn() that sets 2 object properties.

These properties are consistent throughout all the objects I am wanting to initialize.

1) How I call my function

objectType request = factory.create<objectType>(); // <-- This works

1b) From here if I wanted, I could do the following, but is extra code that is repetitive throughout all my objects

request.client_no = CLIENT_NUMBER;

2) Below is my factory fn()

public static T create<T>() where T : new()
{
  T obj = new T();      

  // Here is where I am having trouble setting client_no I will explain in #3         

  return (T)Convert.ChangeType(obj, typeof(T)); // <-- This works returns generic type
}

3) I have tried PropertyInfo to set the object properties as follows

PropertyInfo propertyInfo = obj.GetType().GetProperty("client_no");
propertyInfo.SetValue(obj, CLIENT_NUMBER, null);

I have also tried this

obj.GetType().GetProperty("client_no").SetValue(obj, CLIENT_NUMBER, null); 

And I have tried

T obj = new T();      
var t = typeof(T);
var prop = t.GetProperty("client_no");
prop.SetValue(obj, CLIENT_NUMBER);

4) Here is the error that I am receiving

Object reference not set to an instance of an object

More/Newer information:

So with further research. The 3rd party objects properties do not have getters and setters {get; set;} which is why the GetType() & SetValue() do not work.

My co-worker pointed out that the property is a single assignment which is why we can do

request.client_no = CLIENT_NUMBER;

So my question is how do I set these properties?

Demodave
  • 6,242
  • 6
  • 43
  • 58
  • 5
    It's really not clear what your `updateAccountData` method is meant to achieve. You can't dynamically create variables in C#... if you tell us more about what you're actually trying to achieve, we can help you more. – Jon Skeet Feb 26 '15 at 17:51
  • So does your class `objectName` have the properties you are trying to populate? Or is the idea that those properties don't exist? What do you ultimately expect to do with `request`? – Matt Burland Feb 26 '15 at 17:55
  • @MattBurland They will have it or I can add an option to test if they exist before I add the items. – Demodave Feb 26 '15 at 17:57
  • @MattBurland request would be an object passed into a different function to execute – Demodave Feb 26 '15 at 17:58
  • So then all you need to do is some reflection to use the key in your dictionary to find the appropriate property in your `objectName` object? – Matt Burland Feb 26 '15 at 17:58
  • Are you saying that you want to initialize the properties of an object from values in a Dictionary? That is, your class Foo has a property Bar, and your dictionary has a "Bar" entry, and you want to automatically assign Foo.Bar = d["Bar"]? If so, you want to use reflection: [foo.GetType().GetProperty("Bar")](https://msdn.microsoft.com/en-us/library/kz0a8sxy(v=vs.100).aspx). PropertyInfo has a SetValue method. Done. – 15ee8f99-57ff-4f92-890c-b56153 Feb 26 '15 at 18:00
  • @MattBurland Can you give an example of reflextion. – Demodave Feb 26 '15 at 18:00
  • 2
    @Demodave It's not easily understood. We're all guessing about what you're trying to do. – 15ee8f99-57ff-4f92-890c-b56153 Feb 26 '15 at 18:03
  • @EdPlunkett - What more clarification do you need? – Demodave Feb 26 '15 at 18:04
  • 1
    @Demodave I don't know what you're trying to achieve here but if you think you need to dynamically create new properties on objects then the angle you are coming at it from is wrong. See: [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Ant P Feb 26 '15 at 18:05
  • See my answer. I'm not entirely sure that's what you are looking for and I agree with @AntP that you might be coming at this from the wrong angle. But without more context, it's hard to suggest a better way to tackle it. – Matt Burland Feb 26 '15 at 18:07
  • You *really* should think about refactoring that monster into something less unwieldy. For example, you have several address related fields that could be replaced with an `Address` object or maybe `Contact` object. – Matt Burland Feb 26 '15 at 18:35
  • @MattBurland I do not own the object to refactor it. – Demodave Feb 26 '15 at 18:44

3 Answers3

2

Using reflection, you can do something like this:

public objectName updateAccountData(int accountId, Dictionary<string,string> accountData)
{
    var request = new objectName();
    var t = typeof(objectName);
    foreach (var k in accountData)
    {
        var prop = t.GetProperty(k.Key);   // you might want to check property exists before trying to set it
        prop.SetValue(request,k.Value);
    }
    return request;
}

This assumes that your objectName class already has all the properties you need to populate.

Note that reflection is slower than directly accessing a property, so if this code is in a performance critical section of your application, it might be a problem (profile it). A couple of obvious optimization could be applied pretty easily. For example, you could store all the PropertyInfo objects retrieved from GetProperty in a Dictionary<string,PropertyInfo> to avoid having to keep looking them up. More complicated, you can actually use expression trees to build and compile expressions for property access.

For a field just substitiute t.GetField(k.Key) for GetProperty.

If you don't know if it's a field or a property (or have a mix of both), then you could use GetMember and then examine the MemberType to decide.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • This seems like it has potential. It looks like another problem I had solved with research something similar to PropertyInfo propertyInfo = obj.GetType().GetProperty(key); propertyInfo.SetValue(obj, value, null); – Demodave Feb 26 '15 at 18:15
  • So if it is a performance issue, my object has close 122 properties, what is the best way to scale the objects. I don't want to pass a lot of data. – Demodave Feb 26 '15 at 18:26
  • @Demodave: If you aren't actually populating all 122 properties, but instead only a subset, then it's might not be a problem. If you aren't doing it several thousand times in a tight loop, I wouldn't worry about it too much. But, you might also consider that if you have an object with 122 properties, it might be worth thinking about refactoring it into something more manageable. – Matt Burland Feb 26 '15 at 18:32
  • is there a way to create this as a generic type in case some of the objects property values are not "string" – Demodave Feb 26 '15 at 18:43
  • Pass in a `Dictionary` instead. Now the value can be anything. Note that it will throw exceptions at runtime if you try and set a property to the wrong type. – Matt Burland Feb 26 '15 at 18:45
  • 1
    Don't worry about the performance of this code. You said this is a 3rd party API object, filling in the object via reflection will be orders of magnitude faster than the actual call to the 3rd party API so it will never be your bottleneck. I know Matt only mentioned it for completeness, but notice he also said profile it. If you ever do have a performance problem, profile first then optimize the true problem area. – Ed T Feb 26 '15 at 18:55
  • @EdT "via reflection" - what does that mean? And what does it mean to profile? – Demodave Feb 26 '15 at 19:26
  • 1
    [Reflection](https://msdn.microsoft.com/en-us/library/system.reflection%28v=vs.110%29.aspx) is what the code in the answer uses to find the properties on the object at runtime. Profiling is the act of running a profiler against your running program to find out where any bottlenecks are. You can only know if profiling is needed if you have a specified performance goal. If you don't have any performance goals or if the program is meeting those goals, don't spend time worrying about performance or profiling. – Ed T Feb 27 '15 at 16:58
  • @MattBurland, I've tried this, but am getting an error object reference not set to an instance of object – Demodave Mar 04 '15 at 15:16
  • 1
    @Demodave: Where? Likely `GetProperty` is returning null. As my comment said, you should check that the property actually exists. This can be as simple as `if (prop != null)` before you try `prop.SetValue`. What you should do if `prop == null` is up to you. – Matt Burland Mar 04 '15 at 15:41
  • @MattBurland, I found out that my issue was the 3rd party object does not have a getter or setter is there another way to initialize – Demodave Mar 04 '15 at 15:42
  • 1
    @Demodave: So is it not a property then? Is it a field? In that case use [GetField](https://msdn.microsoft.com/en-us/library/53seyfee(v=vs.110).aspx) instead of `GetProperty`. And then you should be able to [SetValue](https://msdn.microsoft.com/en-us/library/6z33zd7h(v=vs.110).aspx) on the `FieldInfo` returned by `GetField`. – Matt Burland Mar 04 '15 at 15:46
  • @MattBurland, Dude, that was totally it. Man can't believe I've been battling this for days now. Thanks – Demodave Mar 04 '15 at 16:08
2

I see the accepted answer but, here is another way which doesn't involve calling reflection directly and instead uses DLR.

  public static T create<T>() where T : new()
  {
     T obj = new T();
     ((dynamic)obj).client_no = CLIENT_NUMBER;
     return obj;
  }

MSDN Dynamic Language Runtime Overview

The essential part here is that instead of writing the reflection calls yourself the compiler will take care of it. The link above discusses the benefits of using this over making reflection call directly.

Bilal Bashir
  • 1,443
  • 14
  • 18
  • While this answer is probably correct and useful, it is preferred if you include some explanation along with it to explain how it helps to solve the problem. This becomes especially useful in the future, if there is a change (possibly unrelated) that causes it to stop working and users need to understand how it once worked. – Kevin Brown-Silva Mar 05 '15 at 18:48
  • I updated the answer with links to the MSDN DLR documentation. – Bilal Bashir Mar 05 '15 at 19:14
1

SOLVED. Here's how.

My main issue was a miss-understanding, The item I was trying to set was not a property, but rather a field.

To better understand the difference yourself you can check out this.

Here is how I fixed it:

I set the field types of the generic

public static T create<T>() where T : new()
{
  T obj = new T();

  Type myType = typeof(T);
  FieldInfo myFieldInfo = myType.GetField("client_no");
  myFieldInfo.SetValue(obj, CLIENT_NUMBER);                        

  return obj;
}

Special thanks to @MattBurland which led me in the right direction.

Demodave
  • 6,242
  • 6
  • 43
  • 58