0

Consider the following class -

public class User
{
    [Selected]
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    [Selected]
    public int Code { get; set; }
    
    public object GetValue()
    {
        // need to do something here
    }
}

[Selected] here is nothing but a marker attribute. I want GetValue method to return an object which will have the [Selected]-marked properties with corresponding values. That is, in the code below -

private static void Execute()
{
    User user = new User
    {
        Name = "alice",
        Email = "alice@liddell.com",
        Password = "123456",
        Code = 1234
    };

    var value = user.GetValue();
}

value should be an object with two properties Name and Code which should have the values "alice" and 1234 respectively.

After some searching I tried ExpandoObject (which I never used before) -

public object GetValue()
{
    var dictionary = this.GetType().GetProperties().Where(p => p.GetCustomAttribute(typeof(Selected), false) != null).ToDictionary(p => p.Name);
    dynamic expando = new ExpandoObject();
    foreach (var item in dictionary)
    {
        object value = item.Value.GetValue(this);
        ((IDictionary<string, object>)expando).Add(item.Key, value);
    }
    return expando;
}

But it didn't serve my purpose - the client/consumer of value object somehow couldn't read/access the property values.

Any suggestions?

Edit :
There might be a lot of classes like User and the GetValue method will be called from within a generic method. So, at runtime I have no way to know what type the object is and which properties are marked.

atiyar
  • 7,762
  • 6
  • 34
  • 75
  • Since you already know which properties you want at compile time, I'd ditch the attribute and make a different class with the properties you want. e.g. `UserLite`. – itsme86 Sep 04 '20 at 14:42
  • @itsme86, actually at runtime I wouldn't know. There will be a lot of classes, and `GetValue` will be called from within a generic method. I'll update the post to make it clear. – atiyar Sep 04 '20 at 14:46
  • _might be a lot of classes_ - This means that it is impossible to predict types and properties in advance. This means that you must to use a dynamic data structure like a dictionary. POCO is not possible in this scenario. – Alexander Petrov Sep 15 '20 at 18:40

3 Answers3

2

To access the fields by name it is easier to cast the returned object to IDictionary:

    var value = (IDictionary<string, object>) user.GetValue();
    Console.WriteLine(value["Name"]);
2

Simplify your method to this:

public Dictionary<string, object> GetValue()
{
    var dictionary = this.GetType()
        .GetProperties()
        .Where(p => p.GetCustomAttribute(typeof(Selected), false) != null)
        .ToDictionary(p => p.Name, p => p.GetValue(this));

    return dictionary;
}

Use:

var value = user.GetValue(); // value is Dictionary

foreach (var kvp in value)
{
    Console.WriteLine(kvp);
}

If you wish POCO, then you can do it like follows

public class User
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public int Code { get; set; }

    public SelectedUserProperties GetValue()
    {
        return new SelectedUserProperties
        {
            Name = Name,
            Code = Code
        };
    }
}

public class SelectedUserProperties
{
    public string Name { get; set; }
    public int Code { get; set; }
}

It is assumed that the selected properties are known in advance, before compilation.
This makes the marker attribute unnecessary and can be completely removed.

Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • I'm expecting a poco type object to access the properties directly, not a Dictionary – atiyar Sep 15 '20 at 18:20
  • Since the shape of the objects would be defined at runtime, I'm sure `poco` was a bad choice of term on my part. Sorry :( I'd better make the Dictionary work for me, than creating all those classes statically. Thanks a lot :) – atiyar Sep 15 '20 at 19:06
0

What you did works well, you just have to use the keyword dynamic when you call the method GetValue.

    dynamic value = user.GetValue();

If you are using var, value will be of the same type as the return type of your function (i.e object) at compile time. Therefore, if you try to do value.Name your compiler won't allow it because the class Object doesn't have any attribute Name.

dynamic tells your program to do the type checking at runtime.

itectori
  • 136
  • 5