C#泛型类型类无法获取属性值

4
使用C#反射来获取和设置泛型对象中的泛型字段的值。但我找不到任何方法来获取这些字段的属性值。以下是示例代码:
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;
}

问题: 在 (*) 行出现了“null”项。
itemField.GetType() 总是返回 System.Reflection.RtFieldInfo 类型,而不是 Foo 类型。
我已经尝试使用 itemField.FieldType.GetProperty("IsUpdated"),它可以返回正确的属性。但是每当调用 GetValue() 方法时,就会抛出一个错误 "Object does not match target type.",如下所示: itemField.FieldType.GetProperty("IsUpdated").GetValue(itemField, null)
非常感谢任何人提供帮助!
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");
            }
        }

2
基本上,如果您提供了一个 [mcve] 而不是一个不能编译的代码片段,那么帮助您会更加容易。我们无法分辨出哪些错误是在您真正的代码中出现的,哪些只是您在编写问题时添加的。 - Jon Skeet
@HimBromBeere 我怀疑 foreach ( var itemList ... 中有个打字错误,应该是 foreach ( var itemField ... - Fildor
除非您使用 FlattenHierarchy 绑定标志,否则继承的属性不会被 GetProperty 返回:BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy - user47589
@Marc Gravell @HimBromBeere 抱歉!已更新问题。 - Khoa Nguyen
你看过这篇帖子吗:https://stackoverflow.com/questions/1104105/reflection-in-c-sharp-want-a-list-of-the-data-types-of-a-class-fields? - MakePeaceGreatAgain
显示剩余5条评论
3个回答

5

让我们一步一步来理解这个问题:

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

在成员上,您永远不需要使用 .GetType() 方法; 您需要使用 .FieldType.PropertyType (用于属性)。而且,nameof 很好用:

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

(这里的string是一个虚拟值)

接着:

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

你的目标不是 itemField - 它只是给你在该对象上 itemField 的值。因此,将其传递进去,并尽可能将结果作为布尔值处理:

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

最后:

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

再次提醒,该对象是itemList,属性不是一个string

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

如果你创建一个泛型类 Foo<T>,并让它实现一个非泛型接口 IFoo,会更容易一些。
interface IFoo { bool IsUpdated {get; set; } }

然后它变成:

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

最后,需要注意的是如果你没有为FirstNameLastName赋值,它们将为空

感谢您的指导,非常详细。我会尝试的! - Khoa Nguyen

1

您必须使用FieldType属性而不是GetType()itemField处获取Foo类型。

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

编辑:您必须获取字段类型的实例

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

最终结果应该像这样:

最终结果应该像这样:

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);
    }
}

使用itemField.FieldType.GetProperty("IsUpdated"),它只返回了正确的属性。但是每当调用GetValue()方法itemField.FieldType.GetProperty("IsUpdated").GetValue(itemField, null)时,就会抛出错误"对象类型不匹配"。 - Khoa Nguyen
只需使用 GetValue(itemField) 而不是 GetValue(itemField, null) - gabemilani
它会抛出异常吗?将结果与string.Equals进行比较。 - gabemilani
@Kasguest itemField 不是 Foo 的实例,因此会抛出该异常。 - gabemilani

1
让我来谈谈我的看法,试着为你提供另一个解决问题的角度。在这种情况下,我不会使用反射:为什么不使用接口来定义这些对象呢?请保留html标签。
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()

我不确定我是否理解了这个想法,但似乎你过度设计并且没有必要绕过类型检查!


谢谢你的想法,但我必须使用反射来解决这个问题。 - Khoa Nguyen
@Kasguest 顺便说一句,对我来说,你目前的问题似乎是一个 XY 问题。谁知道如果你解释你的问题而不是你尝试的解决方案,我可以说服你在许多情况下避免使用反射:D - Matías Fidemraizer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接