ChatGPT解决这个技术问题 Extra ChatGPT

如何检测 ExpandoObject 上是否存在属性?

在 javascript 中,您可以使用 undefined 关键字检测是否定义了属性:

if( typeof data.myProperty == "undefined" ) ...

您将如何在 C# 中使用带有 ExpandoObject 的动态关键字而不引发异常来执行此操作?

@CodeInChaos:请注意,显示的代码不检查 data.myProperty 的值;它检查 typeof data.myProperty 返回的内容。 data.myProperty 可能存在并设置为 undefined 是正确的,但在这种情况下,typeof 将返回 "undefined" 以外的内容。所以这段代码确实有效。

D
Dykam

根据 MSDN,声明显示它正在实现 IDictionary:

public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
    IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
    IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged

您可以使用它来查看是否定义了成员:

var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
    // expandoObject.SomeMember exists.
}

为了使检查更简单,我重载了 TryGetValue 并使其始终返回 true,如果未定义属性,则将返回值设置为“未定义”。 if( someObject.someParam != "undefined" ) ... 它有效:)
也可能:),但我认为您的意思是“覆盖”而不是重载。
是的。我再次将“未定义”更改为我在其他地方创建的特殊对象的 const 值。它可以防止铸造问题:p
我相信这个解决方案仍然是最新的;不要以反思的代价相信任何人的话——自己测试一下,看看你是否负担得起
@BlueRaja-DannyPflughoeft 是的,是的。如果没有演员表,它将只是一个动态调用,如果您进入内部结构。更具体地说,它是明确实现的:github.com/mono/mono/blob/master/mcs/class/dlr/Runtime/…
C
Chris Moschini

这里需要做一个重要的区分。

这里的大多数答案都特定于问题中提到的 ExpandoObject 。但是一个常见的用法(以及在搜索时遇到这个问题的原因)是在使用 ASP.Net MVC ViewBag 时。这是 DynamicObject 的自定义实现/子类,当您检查任意属性名称是否为空时,它不会引发异常。假设您可以声明一个属性,例如:

@{
    ViewBag.EnableThinger = true;
}

然后假设你想检查它的值,以及它是否被设置——它是否存在。以下是有效的,将编译,不会抛出任何异常,并给你正确的答案:

if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
    // Do some stuff when EnableThinger is true
}

现在摆脱 EnableThinger 的声明。相同的代码可以正常编译和运行。无需反思。

与 ViewBag 不同,如果您在不存在的属性上检查 null,ExpandoObject 将抛出。为了从您的 dynamic 对象中获得 MVC ViewBag 的更温和的功能,您需要使用一个不会抛出异常的动态实现。

您可以简单地使用 MVC ViewBag 中的确切实现:

. . .
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    result = ViewData[binder.Name];
    // since ViewDataDictionary always returns a result even if the key does not exist, always return true
    return true;
}
. . .

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/DynamicViewDataDictionary.cs

您可以在 MVC ViewPage 中看到它被绑定到 MVC 视图中:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs

DynamicViewDataDictionary 优雅行为的关键在于 ViewDataDictionary 上的 Dictionary 实现,这里:

public object this[string key]
{
    get
    {
        object value;
        _innerDictionary.TryGetValue(key, out value);
        return value;
    }
    set { _innerDictionary[key] = value; }
}

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/ViewDataDictionary.cs

换句话说,它总是为所有键返回一个值,而不管它里面有什么——它只是在什么都没有的时候返回 null。但是,ViewDataDictionary 有被绑定到 MVC 模型的负担,所以最好只去掉优雅的字典部分,以便在 MVC 视图之外使用。

在这里发布所有内容太长了——其中大部分只是实现 IDictionary——但这里有一个动态对象(类 DDict),它不会在 Github 上对尚未声明的属性进行空检查:

https://github.com/b9chris/GracefulDynamicDictionary

如果您只想通过 NuGet 将其添加到您的项目中,则其名称为 GracefulDynamicDictionary


为什么你对 DynamicDictionary 投了反对票,因为它不使用反射呢?
然后您可以投票,因为这是相同的解决方案:)
它肯定不是同一个解决方案。
这,这正是我在这里的原因,我无法弄清楚为什么某些代码(viewbag)没有破坏。谢谢你。
只需将您的包与 Newtonsoft JSON 反序列化器结合使用,它就像一个魅力!动态的东西 = JsonConvert.DeserializeObject(jsonStuff, new ExpandoObjectConverter());
M
Maxime

我想创建一个扩展方法,以便可以执行以下操作:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}

...但是您不能根据 C# 5 文档(更多信息 here)在 ExpandoObject 上创建扩展。

所以我最终创建了一个类助手:

public static class ExpandoObjectHelper
{
    public static bool HasProperty(ExpandoObject obj, string propertyName)
    {
        return obj != null && ((IDictionary<String, object>)obj).ContainsKey(propertyName);
    }
}

要使用它:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}

对 ExpandoObject 扩展的有用评论和链接投票。
obj 可以为 null,因此: return obj != null && ((IDictionary)obj).ContainsKey(propertyName);
O
Oleksandr G

更新:您可以使用委托并尝试从动态对象属性中获取值(如果存在)。如果没有属性,只需捕获异常并返回 false。

看一下,它对我来说很好用:

class Program
{
    static void Main(string[] args)
    {
        dynamic userDynamic = new JsonUser();

        Console.WriteLine(IsPropertyExist(() => userDynamic.first_name));
        Console.WriteLine(IsPropertyExist(() => userDynamic.address));
        Console.WriteLine(IsPropertyExist(() => userDynamic.last_name));
    }

    class JsonUser
    {
        public string first_name { get; set; }
        public string address
        {
            get
            {
                throw new InvalidOperationException("Cannot read property value");
            }
        }
    }

    static bool IsPropertyExist(GetValueDelegate getValueMethod)
    {
        try
        {
            //we're not interesting in the return value. What we need to know is whether an exception occurred or not
            getValueMethod();
            return true;
        }
        catch (RuntimeBinderException)
        {
            // RuntimeBinderException occurred during accessing the property
            // and it means there is no such property         
            return false;
        }
        catch
        {
            //property exists, but an exception occurred during getting of a value
            return true;
        }
    }

    delegate string GetValueDelegate();
}

代码的输出如下:

True
True
False

@marklam 当您不知道导致异常的原因时,捕获所有异常是很糟糕的。在我们的情况下,这没关系,因为我们预计可能没有字段。
如果您知道导致异常的原因,您还必须知道它的类型,所以 catch (WhateverException) 否则即使您遇到意外异常,您的代码也会默默地继续运行 - 例如 OutOfMemoryException。
您可以将任何 getter 传递给 IsPropertyExist。在此示例中,您知道可以抛出 InvalidOperationException。实际上,您不知道可能会引发什么异常。 +1 以抵制货物崇拜。
如果性能很重要,则此解决方案是不可接受的,例如,如果在具有 500 次以上迭代的循环中使用,它加起来会导致数秒的延迟。每次捕获异常时,必须将堆栈复制到异常对象
回复:性能:附加的调试器和 Console.WriteLine 是慢位。这里 10,000 次迭代需要不到 200 毫秒(每次迭代有 2 个异常)。没有例外的相同测试只需要几毫秒。这意味着,如果您希望使用此代码很少会丢失属性,或者如果您调用它的次数有限,或者可以缓存结果,那么请意识到一切都有它的位置,并且没有任何过度- 这里的反刍警告很重要。
C
Community

我最近回答了一个非常相似的问题:How do I reflect over the members of dynamic object?

很快,ExpandoObject 并不是您可能获得的唯一动态对象。反射适用于静态类型(不实现 IDynamicMetaObjectProvider 的类型)。对于确实实现了这个接口的类型,反射基本上是没用的。对于 ExpandoObject,您可以简单地检查该属性是否定义为底层字典中的键。对于其他实现,它可能具有挑战性,有时唯一的方法是处理异常。有关详细信息,请点击上面的链接。


V
Vokinneberg

为什么您不想使用反射来获取类型属性集?像这样

 dynamic v = new Foo();
 Type t = v.GetType();
 System.Reflection.PropertyInfo[] pInfo =  t.GetProperties();
 if (Array.Find<System.Reflection.PropertyInfo>(pInfo, p => { return p.Name == "PropName"; }).    GetValue(v,  null) != null))
 {
     //PropName initialized
 } 

我不确定这是否会返回动态添加的属性,我的猜测是它返回 Dynamic 对象的方法。
r
realdanielbyrne

此扩展方法检查属性是否存在,然后返回值或 null。如果您不希望您的应用程序抛出不必要的异常,这将很有用,至少您可以提供帮助。

    public static object Value(this ExpandoObject expando, string name)
    {
        var expandoDic = (IDictionary<string, object>)expando;
        return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
    }

如果可以这样使用:

  // lookup is type 'ExpandoObject'
  object value = lookup.Value("MyProperty");

或者,如果您的局部变量是“动态的”,则必须先将其转换为 ExpandoObject。

  // lookup is type 'dynamic'
  object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");

I
Ian Kemp

根据您的用例,如果可以将 null 视为与未定义相同,您可以将 ExpandoObject 转换为 DynamicJsonObject

    dynamic x = new System.Web.Helpers.DynamicJsonObject(new ExpandoObject());
    x.a = 1;
    x.b = 2.50;
    Console.WriteLine("a is " + (x.a ?? "undefined"));
    Console.WriteLine("b is " + (x.b ?? "undefined"));
    Console.WriteLine("c is " + (x.c ?? "undefined"));

输出:

a is 1
b is 2.5
c is undefined

R
Ramy Yousef
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");

S
Softlion

嘿,伙计们,停止使用反射来处理所有消耗大量 CPU 周期的事情。

这是解决方案:

public class DynamicDictionary : DynamicObject
{
    Dictionary<string, object> dictionary = new Dictionary<string, object>();

    public int Count
    {
        get
        {
            return dictionary.Count;
        }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        string name = binder.Name;

        if (!dictionary.TryGetValue(binder.Name, out result))
            result = "undefined";

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dictionary[binder.Name] = value;
        return true;
    }
}

这显示了如何实现一个动态对象,而不是如何在动态对象上看到一个属性存在。
您可以通过对相关属性进行空检查来检查动态实例是否具有属性。
“这显示了如何实现动态对象”:是的,事实上它是。这个问题的解决方案是:没有通用的解决方案,因为它取决于实现。
@Softlion 不,解决方案是我们必须停止使用的东西
@Softlion Tryxxx 方法的意义何在? TryGet 在找不到该属性时永远不会返回 false,因此您仍然需要检查结果。回报是没有用的。在 TrySet 中,如果 key 不存在,那么它会抛出异常而不是返回 false。我不明白为什么你甚至会用这个作为答案,如果你自己在这里写评论“这个问题的解决方案是:没有通用的解决方案,因为它取决于实现”,这也是不正确的。查看 Dykam 的答案以获得真正的解决方案。
V
Venkat

试试这个

public bool PropertyExist(object obj, string propertyName)
{
 return obj.GetType().GetProperty(propertyName) != null;
}

这将检查隐藏在动态名称下的对象的属性是否存在,这是一个实现细节。在发布之前,您是否在真实代码中验证了您的解决方案?它根本不应该工作。
我在实时场景中使用了这段代码。它运作良好。
一直给我 null,即使该属性存在。
它适用于基本对象,但不适用于 ExpandoObjects。动力学,不确定。
确认一下,这也不适用于 dynamic 对象(总是返回 null)。