如何检查 C# 中的动态匿名类型上是不是存在属性?

Posted

技术标签:

【中文标题】如何检查 C# 中的动态匿名类型上是不是存在属性?【英文标题】:How do I check if a property exists on a dynamic anonymous type in c#?如何检查 C# 中的动态匿名类型上是否存在属性? 【发布时间】:2012-04-14 22:41:53 【问题描述】:

我有一个匿名类型对象,我从方法中接收到动态对象 我想检查该对象上是否存在属性。

....
var settings = new 
                   Filename="temp.txt",
                   Size=10

...

function void Settings(dynamic settings) 
var exists = IsSettingExist(settings,"Filename")

我将如何实现 IsSettingExist ?

【问题讨论】:

How to detect if a property exists on an ExpandoObject?的可能重复 如果你发现自己严重依赖动态对象,那么可能值得看看 F# - 顺便说一句,Nice Avatar 【参考方案1】:
  public static bool IsPropertyExist(dynamic settings, string name)
  
    if (settings is ExpandoObject)
      return ((IDictionary<string, object>)settings).ContainsKey(name);

    return settings.GetType().GetProperty(name) != null;
  

  var settings = new Filename = @"c:\temp\q.txt";
  Console.WriteLine(IsPropertyExist(settings, "Filename"));
  Console.WriteLine(IsPropertyExist(settings, "Size"));

输出:

 True
 False

【讨论】:

这不适用于动态对象。它总是返回 null。 @evilom @Shikasta_Kashti 您是否尝试将此方法与 MVC ViewBag 一起使用?如果是这样,请参阅***.com/a/24192518/70345 @Gaspa79。这是一个不常见的编码约定。有些人喜欢在所有布尔属性上加上“Is”前缀。这样的一致性可以防止您不得不猜测标识符的前几个字符(之后,Intellisense 起作用),但代价是在这种情况下会产生一些尴尬的英语。 我发现 Is 前缀的无效动词时态比使用 HasProperty 更令人困惑。我还要说,在 C♯ 中使用这样的语法错误前缀实际上是非惯用的。 ExpandoObject 与匿名类型不同。我错了吗?【参考方案2】:
public static bool HasProperty(dynamic obj, string name)

    Type objType = obj.GetType();

    if (objType == typeof(ExpandoObject))
    
        return ((IDictionary<string, object>)obj).ContainsKey(name);
    

    return objType.GetProperty(name) != null;

【讨论】:

objType.GetProperty(name) != null; 对确实存在的属性返回 null objType.GetProperty(name) != null 将始终返回一个bool,它(根据定义)永远不可能是null @AlexMcMillan 不确定你住在哪个维度,Type.GetProperty(string) 对于不存在的属性返回 null 以外的任何内容。 @IanKemp,AlexMcMillan 实际上在回复 MatasVaitkevicius 评论时说 objType.GetProperty(name) != null【参考方案3】:

如果您可以控制创建/传递设置对象,我建议改用 ExpandoObject。

dynamic settings = new ExpandoObject();
settings.Filename = "asdf.txt";
settings.Size = 10;
...

function void Settings(dynamic settings)

    if ( ((IDictionary<string, object>)settings).ContainsKey("Filename") )
        .... do something ....

【讨论】:

我无法更改,我可以转换为 ExpendoObject 吗?【参考方案4】:

这适用于匿名类型,ExpandoObjectNancy.DynamicDictionary 或其他任何可以转换为 IDictionary&lt;string, object&gt; 的类型。

    public static bool PropertyExists(dynamic obj, string name) 
        if (obj == null) return false;
        if (obj is IDictionary<string, object> dict) 
            return dict.ContainsKey(name);
        
        return obj.GetType().GetProperty(name) != null;
    

【讨论】:

很好的解决方案。在将 JSON 字符串转换为 JObject 时,我需要再添加一条 IF 语句......"if (obj is Newtonsoft.Json.Linq.JObject) return ((Newtonsoft.Json.Linq.JObject)obj).ContainsKey(name); " 也为我工作。精彩的回答赛斯雷诺。我还添加了“if (obj is Newtonsoft.Json.Linq.JObject) return ((Newtonsoft.Json.Linq.JObject)obj).ContainsKey(name);”在 rr789 建议的上述功能中。因此,还请编辑您的答案以包含它。 谢谢@BrijeshKumarTripathi!这正是我的场景。【参考方案5】:

这对我有用-:

  public static bool IsPropertyExist(dynamic dynamicObj, string property)
       
           try
           
               var value=dynamicObj[property].Value;
               return true;
           
           catch (RuntimeBinderException)
           

               return false;
           

       

【讨论】:

允许异常发生然后捕获它们不是首选的解决方案,因为抛出和捕获相关的开销很大。这只是最后的手段。例外适用于在执行过程中不应该发生的情况,例如网络不可用。这里有更好的解决方案。 当值实际存在时,RuntimeBinderExceptiondynamicObj[property].Value 失败......var value = dynamicObj[property] 就足够了......当它不存在时,KeyNotFoundException 上的 Dictionary 被抛出.. . 见下文... 在业务逻辑中使用异常是不可接受的解决方案。 1 年级,第 2 学期。【参考方案6】:

合并和修复来自 Serj-TM 和 user3359453 的答案,使其适用于 ExpandoObject 和 DynamicJsonObject。这对我有用。

public static bool HasPropertyExist(dynamic settings, string name)

    if (settings is System.Dynamic.ExpandoObject)
        return ((IDictionary<string, object>)settings).ContainsKey(name);

    if (settings is System.Web.Helpers.DynamicJsonObject)
    try
    
        return settings[name] != null;
    
    catch (KeyNotFoundException)
    
        return false;
    


    return settings.GetType().GetProperty(name) != null;

【讨论】:

【参考方案7】:

使用反射,这是我使用的功能:

public static bool doesPropertyExist(dynamic obj, string property)

    return ((Type)obj.GetType()).GetProperties().Where(p => p.Name.Equals(property)).Any();

那么..

if (doesPropertyExist(myDynamicObject, "myProperty"))
    // ...

【讨论】:

GetProperties() 没有列出 DynamicObject 上的动态成员。为此有一个专用函数 GetDynamicMemberNames()。 先使用 lambda 表达式 Where,然后使用 Any 是多余的,因为您也可以在 Any 中制定过滤表达式。【参考方案8】:

我遇到的动态是Newtonsoft.Json.Linq.JObject而不是IDictionary

如果它有效,我添加了额外的内容。

public static bool PropertyExists(dynamic obj, string name)

    if (obj == null) return false;

    else if (obj is IDictionary<string, object> dict)
    
        return dict.ContainsKey(name);
    

    else if (obj is Newtonsoft.Json.Linq.JObject jObject)
    
        return jObject.ContainsKey(name);
    

    else
    
        return obj.GetType().GetProperty(name) != null;
    

【讨论】:

【参考方案9】:

上述解决方案均不适用于来自Jsondynamic,但是我设法通过更改引发的异常类型(KeyNotFoundException 而不是RuntimeBinderException)来转换一个带有Try catch(@user3359453)的解决方案变成真正有效的东西......

public static bool HasProperty(dynamic obj, string name)
    
        try
        
            var value = obj[name];
            return true;
        
        catch (KeyNotFoundException)
        
            return false;
        
    

希望这可以为您节省一些时间。

【讨论】:

不建议对此类事情使用例外。应该使用诸如转换为 JObject 并使用 .Property() != null 之类的东西【参考方案10】:

如果有人需要处理来自 Json 的动态对象,我修改了 Seth Reno 的答案以处理从 NewtonSoft.Json.JObjcet 反序列化的动态对象。

public static bool PropertyExists(dynamic obj, string name)
    
        if (obj == null) return false;
        if (obj is ExpandoObject)
            return ((IDictionary<string, object>)obj).ContainsKey(name);
        if (obj is IDictionary<string, object> dict1)
            return dict1.ContainsKey(name);
        if (obj is IDictionary<string, JToken> dict2)
            return dict2.ContainsKey(name);
        return obj.GetType().GetProperty(name) != null;
    

【讨论】:

【参考方案11】:

为了扩展@Kuroro的答案,如果您需要测试属性是否为空,下面应该可以工作。

public static bool PropertyExistsAndIsNotNull(dynamic obj, string name)

    if (obj == null) return false;
    if (obj is ExpandoObject)
    
        if (((IDictionary<string, object>)obj).ContainsKey(name))
            return ((IDictionary<string, object>)obj)[name] != null;
        return false;
    
    if (obj is IDictionary<string, object> dict1)
    
        if (dict1.ContainsKey(name))
            return dict1[name] != null;
        return false;
    
    if (obj is IDictionary<string, JToken> dict2)
    
        if (dict2.ContainsKey(name))
            return (dict2[name].Type != JTokenType.Null && dict2[name].Type != JTokenType.Undefined);
        return false;
    
    if (obj.GetType().GetProperty(name) != null)
        return obj.GetType().GetProperty(name).GetValue(obj) != null;
    return false;

【讨论】:

【参考方案12】:

这也适用于 DynamicJsonObject:

  public static bool PropertyExists(dynamic settings, string name)
  
    if (settings is ExpandoObject)
      return ((IDictionary<string, object>)settings).ContainsKey(name);
    else if (settings is DynamicJsonObject)
      return ((DynamicJsonObject)settings).GetDynamicMemberNames().Contains(name);

    return settings.GetType().GetProperty(name) != null;
  

【讨论】:

【参考方案13】:
    public static void Test()
    
        int LOOP_LENGTH = 100000000;
        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("doesPropertyExist");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.doesPropertyExist(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.doesPropertyExist(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        

        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("HasProperty");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.HasProperty(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.HasProperty(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        


        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("IsPropertyExist");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.IsPropertyExist(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.IsPropertyExist(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        

        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("IsPropertyExistBinderException");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.IsPropertyExistBinderException(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.IsPropertyExistBinderException(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        


        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("PropertyExists");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.PropertyExists(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.PropertyExists(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        


        
            long first_memory = GC.GetTotalMemory(true);
            var stopWatch = Stopwatch.StartNew();

            Console.WriteLine("PropertyExistsJToken");

            dynamic testdo = new  A = 1, B = (string)null, C = "A" ;
            for (int i = 0; i < LOOP_LENGTH; i++)
            
                if (!TestDynamic.PropertyExistsJToken(testdo, "A"))
                
                    Console.WriteLine("throw find");
                    break;
                

                if (TestDynamic.PropertyExistsJToken(testdo, "ABC"))
                
                    Console.WriteLine("throw not find");
                    break;
                
            
            stopWatch.Stop();
            var last_memory = GC.GetTotalMemory(true);
            Console.WriteLine($" Time:stopWatch.Elapsed.TotalSecondss\t Memory:last_memory - first_memory");
        




    

    public static bool IsPropertyExist(dynamic settings, string name)
    
        if (settings is ExpandoObject)
            return ((IDictionary<string, object>)settings).ContainsKey(name);

        return settings.GetType().GetProperty(name) != null;
    

    public static bool HasProperty(dynamic obj, string name)
    
        Type objType = obj.GetType();

        if (objType == typeof(ExpandoObject))
        
            return ((IDictionary<string, object>)obj).ContainsKey(name);
        

        return objType.GetProperty(name) != null;
    


    public static bool PropertyExists(dynamic obj, string name)
    
        if (obj == null) return false;
        if (obj is IDictionary<string, object> dict)
        
            return dict.ContainsKey(name);
        
        return obj.GetType().GetProperty(name) != null;
    

    // public static bool HasPropertyExist(dynamic settings, string name)
    // 
    //     if (settings is System.Dynamic.ExpandoObject)
    //         return ((IDictionary<string, object>)settings).ContainsKey(name);
    //     if (settings is DynamicJsonObject)
    //         try
    //         
    //             return settings[name] != null;
    //         
    //         catch (KeyNotFoundException)
    //         
    //             return false;
    //         
    //     return settings.GetType().GetProperty(name) != null;
    // 

    public static bool IsPropertyExistBinderException(dynamic dynamicObj, string property)
    
        try
        
            var value = dynamicObj[property].Value;
            return true;
        
        catch (RuntimeBinderException)
        

            return false;
        

    

    public static bool HasPropertyFoundException(dynamic obj, string name)
    
        try
        
            var value = obj[name];
            return true;
        
        catch (KeyNotFoundException)
        
            return false;
        
    


    public static bool doesPropertyExist(dynamic obj, string property)
    
        return ((Type)obj.GetType()).GetProperties().Where(p => p.Name.Equals(property)).Any();
    

    public static bool PropertyExistsJToken(dynamic obj, string name)
    
        if (obj == null) return false;
        if (obj is ExpandoObject)
            return ((IDictionary<string, object>)obj).ContainsKey(name);
        if (obj is IDictionary<string, object> dict1)
            return dict1.ContainsKey(name);
        if (obj is IDictionary<string, JToken> dict2)
            return dict2.ContainsKey(name);
        return obj.GetType().GetProperty(name) != null;
    

    // public static bool PropertyExistsJsonObject(dynamic settings, string name)
    // 
    //     if (settings is ExpandoObject)
    //         return ((IDictionary<string, object>)settings).ContainsKey(name);
    //     else if (settings is DynamicJsonObject)
    //         return ((DynamicJsonObject)settings).GetDynamicMemberNames().Contains(name);

    //     return settings.GetType().GetProperty(name) != null;
    // 

确实存在属性

时间:59.5907507s 内存:403680

有属性

时间:30.8231781s 内存:14968

IsPropertyExist

时间:39.6179575s 内存:97000

IsPropertyExistBinderException 抛出查找

属性存在

时间:56.009761s 内存:13464

PropertyExistsJToken

时间:61.6146953s 内存:15952

【讨论】:

【参考方案14】:

为了节省其他人一些时间,这个答案涵盖了很多使用 Newtonstoft Json 动态反序列化谷歌搜索这个问题的人:

dynamic dynamicObject = JsonConvert.DeserializeObject<dynamic>(json);
if (IsContainsKey(dynamicObject, "searchText"))
    searchText = dynamicObject.searchText;

bool IsContainsKey(dynamic newtonsoftDynamic, string propertyName) 
    return (newtonsoftDynamic as JObject).ContainsKey(propertyName);

或者只是在代码中:

JObject obj = dynamicObject as JObject;
string searchText = string.Empty;
if (obj.ContainsKey("searchText"))
    searchText = obj.Value<string>("searchText");

没有例外,通过 Newtonsoft 进行正确处理,但您仍然可以将 dynamicObject.xxx 用于您知道将始终存在的属性。

【讨论】:

以上是关于如何检查 C# 中的动态匿名类型上是不是存在属性?的主要内容,如果未能解决你的问题,请参考以下文章

C#“动态”无法访问在另一个程序集中声明的匿名类型的属性

具有匿名类型的 C# LINQ 构建表达式

C#复习总结匿名类型由来

如何在 C# 中迭代​​匿名对象的属性?

如何检查对象是不是具有某些方法/属性?

如何在 C# 中的 WinRAR、7Zip、Zip、Tar、Winzip 中检查文件是不是存在