简体   繁体   中英

c# generic type class cannot get the property value

Using c# reflection to get & set the value of the generic fields inside a generic object. But I cannot find any way to get the property value of these fields . The code example below:

public class Foo<T>
{
   public bool IsUpdated { get; set; } 
}

public abstract class ValueObjec<T>
{
   public string InnerValue { get; set; } 
}

public class ItemList: ValueObject<ItemList>
{
   public Foo<string> FirstName;

   public Foo<string> LastName;
}

Problem Getting the 'null' item at the line (*).

itemField.GetType() always return the System.Reflection.RtFieldInfo type but not the Foo type.

I already tried using itemField.FieldType.GetProperty("IsUpdated"), it worked when returned the correct property. But threw an error "Object does not match target type." whenever call GetValue() method itemField.FieldType.GetProperty("IsUpdated").GetValue(itemField, null)

Much more appreciated if getting help from anyone!

var itemList = new ItemList();
foreach (var itemField in itemList.GetType().GetFields())
{
    var isUpdated = "false";
    var isUpdatedProp = itemField.GetType().GetProperty("IsUpdated"); // (*) return null from here
    if (isUpdatedProp != null)
    {
        isUpdated = isUpdatedProp.GetValue(itemField, null).ToString();
        if (isUpdated == "false") isUpdatedProp.SetValue(itemField, "true");
    }
}

foreach (var itemField in itemList.GetType().GetFields())
        {
            var isUpdated = "false";
            var isUpdatedProp = itemField.FieldType.GetProperty("IsUpdated");
            if (isUpdatedProp != null)
            {
                isUpdated = isUpdatedProp.GetValue(itemField, null).ToString(); (*) // throw error "Object does not match target type"
                if (isUpdated == "false") isUpdatedProp.SetValue(itemField, "true");
            }
        }

let's unpack this one thing at a time:

var isUpdatedProp = itemField.GetType().GetProperty("IsUpdated");

You should never need to use .GetType() on a member; you want .FieldType or .PropertyType (for properties). And nameof is great:

var isUpdatedProp = itemField.FieldType.GetProperty(nameof(Foo<string>.IsUpdated));

( string here is a dummy)

Then:

 isUpdated = isUpdatedProp.GetValue(itemField, null).ToString();

It isn't itemField that is your object - that's whatever the value of itemField on that object gives you. So pass that in; and treat the result as a boolean , if possible:

var isUpdated = false;
object foo = itemField.GetValue(itemList);
...
isUpdated = (bool)isUpdatedProp.GetValue(foo, null);

Finally:

if (isUpdated == "false") isUpdatedProp.SetValue(itemField, "true");

Once again, the object is itemList , and the property isn't a string

if (!isUpdated) isUpdatedProp.SetValue(foo, true);

It would be easier if you made Foo<T> : IFoo where IFoo is a non-generic interface:

interface IFoo { bool IsUpdated {get; set; } }

Then it becomes:

var foo = (IFoo)itemField.GetValue(itemList);
if(!foo.IsUpdated) foo.IsUpdated = true;

Finally, note that FirstName and LastName will be null if you haven't assigned them anything.

You must use the FieldType property instead of GetType() at itemField to get Foo type.

https://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.fieldtype(v=vs.110).aspx

EDIT: You must get the instance of the field type

var foo = itemField.GetValue(itemList);
var isUpdated = isUpdatedProp.GetValue(foo);

Final result should be like this:

var itemList = new ItemList();
foreach (var itemField in itemList.GetType().GetFields())
{
    var isUpdatedProp = itemField.FieldType.GetProperty("IsUpdated"); 
    if (isUpdatedProp != null)
    {
        var foo = itemField.GetValue(itemList);
        isUpdated = (bool)isUpdatedProp.GetValue(foo);
        if (!isUpdated) isUpdatedProp.SetValue(foo, true);
    }
}

Let me throw my two cents on trying to give you another point of view to solve the problem.

I wouldn't use reflection for this: why don't you type those objects with interfaces?

public interface IUpdatable
{
     bool IsUpdated { get; set; }
}

public interface IManyUpdatable 
{
     IEnumerable<IUpdatable> Updatables { get; }
}

public static class UpdatableExtensions 
{
     public static void ToggleUpdated(this IUpdatable updatable) 
     {
          if(updatable != null)
               updatable.IsUpdated = !updatable.IsUpdated
     }

     public static void ToggleUpdatables(this IEnumerable<IUpdatable> updatables)
     {
          foreach(var updatable in updatables) 
                 updatable.ToggleUpdated()

     }
}

public class Foo<T> : IUpdatable
{
   public bool IsUpdated { get; set; }
}

public class ItemList: ValueObject<ItemList>
{
   public Foo<string> FirstName;

   public Foo<string> LastName;

   IEnumerable<IUpdatable> IManyUpdatable.Updatables => new [] {  FirstName, LastName }
}

// ... somewhere in your code base:

itemList.ToggleUpdatables()

I'm not sure if I've caught the idea, but it seems like you're overengineering and bypassing typing for no reason!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM