【问题标题】:C#: How to get all public (both get and set) string properties of a typeC#:如何获取一个类型的所有公共(获取和设置)字符串属性
【发布时间】:2010-10-23 21:54:58
【问题描述】:

我正在尝试创建一个方法,该方法将遍历泛型对象列表并替换其所有类型为 string 的属性,该属性要么为 null,要么为空。

如何做到这一点的好方法?

到目前为止,我有这种...外壳...:

public static void ReplaceEmptyStrings<T>(List<T> list, string replacement)
{
    var properties = typeof(T).GetProperties( -- What BindingFlags? -- );

    foreach(var p in properties)
    {
        foreach(var item in list)
        {
            if(string.IsNullOrEmpty((string) p.GetValue(item, null)))
                p.SetValue(item, replacement, null);
        }
    }
}

那么,我如何找到一个类型的所有属性:

  • 类型为string

  • 已公开get

  • 已公开set

    ?


我做了这个测试类:

class TestSubject
{
    public string Public;
    private string Private;

    public string PublicPublic { get; set; }
    public string PublicPrivate { get; private set; }
    public string PrivatePublic { private get; set; }
    private string PrivatePrivate { get; set; }
}

以下不起作用:

var properties = typeof(TestSubject)
        .GetProperties(BindingFlags.Instance|BindingFlags.Public)
        .Where(ø => ø.CanRead && ø.CanWrite)
        .Where(ø => ø.PropertyType == typeof(string));

如果我打印出我到达那里的那些属性的名称,我会得到:

公共公共 公私 私人公共

换句话说,我得到的两个属性太多了。


注意:这可能会以更好的方式完成...使用嵌套的 foreach 和反射等等...但是如果您有任何好的替代想法,请告诉我知道因为我想学习!

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    您的代码已重写。不使用 LINQ 也不使用 var。

    public static void ReplaceEmptyStrings<T>(List<T> list, string replacement)
    {
        PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    
        foreach (PropertyInfo p in properties)
        {
            // Only work with strings
            if (p.PropertyType != typeof(string)) { continue; }
    
            // If not writable then cannot null it; if not readable then cannot check it's value
            if (!p.CanWrite || !p.CanRead) { continue; }
    
            MethodInfo mget = p.GetGetMethod(false);
            MethodInfo mset = p.GetSetMethod(false);
    
            // Get and set methods have to be public
            if (mget == null) { continue; }
            if (mset == null) { continue; }
    
            foreach (T item in list)
            {
                if (string.IsNullOrEmpty((string)p.GetValue(item, null)))
                {
                    p.SetValue(item, replacement, null);
                }
            }
        }
    }
    

    【讨论】:

    • 您的示例首先将替换 all 值,其次,CanWrite 属性似乎不像我们认为的那样工作... = /
    • CanWrite 确实按应有的方式工作。你能解释一下是什么让你产生相反的想法吗?
    • 如我的示例所示,声明为公共属性的公共属性,即公共字符串Something {get; private set;},CanRead 和 CanWrite 都会返回 true,即使我不应该写,因为 setter 是私有的。
    • 好的,我想我明白你的意思了。 CanWrite 和 CanRead 只是检查是否将 set 和 get 访问器分配给属性。检查 get 和 set 方法的公开性以确定您是否可以调用它们。我已经更新了我的代码。
    • 啊啊,我们走了!谢谢=)
    【解决方案2】:

    您将使用BindingFlags.Public | BindingFlags.Instance 找到这些属性。然后,您需要通过检查 CanWrite 和 CanRead 属性来检查每个 PropertyInfo 实例,以确定它们是否可读和/或可写。

    更新:代码示例

    PropertyInfo[] props = yourClassInstance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < props.Length; i++)
    {
        if (props[i].PropertyType == typeof(string) && props[i].CanWrite)
        {
            // do your update
        }
    }
    

    在您更新后,我对其进行了更详细的研究。如果您还检查 GetGetMethod 和 GetSetMethod 返回的 MethodInfo 对象,我认为您将达到目标;

     var properties = typeof(TestSubject).GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(ø => ø.CanRead && ø.CanWrite)
            .Where(ø => ø.PropertyType == typeof(string))
            .Where(ø => ø.GetGetMethod(true).IsPublic)
            .Where(ø => ø.GetSetMethod(true).IsPublic);
    

    默认情况下,这两个方法只返回公共 getter 和 setter(在这种情况下会冒 NullReferenceException 的风险),但是像上面那样传递 true 会使它们也返回私有的。然后您可以检查IsPublic(或IsPrivate)属性。

    【讨论】:

    • 这不起作用。看我的例子。即使 get 或 set 是私有的,它表示它可以读写,只要其中一个是公共的。
    【解决方案3】:

    如果您不指定任何绑定标志,您将获得公共的实例属性——这正是您想要的。但随后您将需要检查 PropertyInfo 对象上的 PropertyType 是否为 String 类型。除非您事先知道,否则您还需要检查该属性是否如@Fredrik 指示的那样可读/可写。

    using System.Linq;
    
    public static void ReplaceEmptyStrings<T>(List<T> list, string replacement)
    {
        var properties = typeof(T).GetProperties()
                                  .Where( p => p.PropertyType == typeof(string) );
        foreach(var p in properties)
        {
            foreach(var item in list)
            {
                if(string.IsNullOrEmpty((string) p.GetValue(item, null)))
                    p.SetValue(item, replacement, null);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      http://jefferytay.wordpress.com/2010/05/03/simple-and-useful-tostring/

      对于一个 tostring 覆盖方法,它允许您获取类的所有属性

      【讨论】:

        【解决方案5】:

        BindingFlags.Public | BindingFlags.Instance 应该这样做

        GetSetMethod()

        【讨论】:

          【解决方案6】:

          我建议采用不同的方法:AOP
          您可以拦截设置器并将所需的值设置为有效值。使用PostSharp 非常简单。

          【讨论】:

          • 正如我所说,这是一种不同的方法。我建议不要在设置后更改值 ,而是拦截设置器,并在需要时更改值。使用 PostSharp 时,您可以编写属性并将它们与您的属性一起使用。
          • 你将如何拦截二传手?那么你不需要访问类或覆盖属性或其他东西吗?
          【解决方案7】:

          我同意其他答案,但我更喜欢重构搜索本身以便使用 Linq 轻松查询,因此查询可能如下:

                  var asm = Assembly.GetExecutingAssembly();
                  var properties = (from prop
                                        in asm.GetType()
                                          .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                    where 
                                      prop.PropertyType == typeof (string) && 
                                      prop.CanWrite && 
                                      prop.CanRead
                                    select prop).ToList();
                  properties.ForEach(p => Debug.WriteLine(p.Name));
          

          我以 Assembly 类型为例,它没有读/写字符串属性,但如果相同的代码搜索只读属性,结果将是:

          • 代码库
          • 转义代码库
          • 全名
          • 位置
          • ImageRuntimeVersion

          哪些是字符串 只读程序集类型属性

          【讨论】:

            猜你喜欢
            • 2011-01-02
            • 1970-01-01
            • 2014-03-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-04
            • 1970-01-01
            相关资源
            最近更新 更多