比较 C# 中的动态对象

发布于 2024-12-01 11:45:24 字数 377 浏览 1 评论 0 原文

比较两个任意动态对象是否相等的最佳方法是什么?例如这两个对象。

dynamic obj1 = new ExpandoObject();
obj1.Name = "Marcus";
obj1.Age = 39;
obj1.LengthInMeters = 1.96;

dynamic obj2 = AMethodReturningADynamic();
obj2.Name = "Marcus";
obj2.Age = 39;
obj2.LengthInMeters = 1.96;

Assert.AreEqual(obj1, obj2); // ?

或者有没有办法以列表的形式获取实际属性及其值?例如,要从动态类型创建 ExpandoObject?

What is the best way to compare two arbitrary dynamic objects for equality? For example these two objects.

I.e.

dynamic obj1 = new ExpandoObject();
obj1.Name = "Marcus";
obj1.Age = 39;
obj1.LengthInMeters = 1.96;

dynamic obj2 = AMethodReturningADynamic();
obj2.Name = "Marcus";
obj2.Age = 39;
obj2.LengthInMeters = 1.96;

Assert.AreEqual(obj1, obj2); // ?

Or is there a way to get the actual properties and their values as lists? To create an ExpandoObject from a dynamic type for example?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

囚你心 2024-12-08 11:45:24

当您没有编译器的帮助时,用于动态调用任意动态对象 (IDynamicMetaObjectProvider) 上的方法和属性的 Microsoft API 并不容易使用。您可以使用 Dynamitey (通过 nuget)来完全简化此操作。它有一个静态函数 Dynamic.InvokeGet 来仅使用目标和属性名称来调用属性的 getter。

要获取动态对象的属性列表,有一些陷阱,因为动态对象必须支持它(如果它是一个 DynamicObject,意味着实现 GetDynamicMemberNames,Expando 支持它,但随机 IDynamicMetaObjectProvider 可能不支持它,只返回一个空列表)。 Dynamitey 也有一种简化获取这些名称的方法, 动态.GetMemberNames

这两个函数都为您提供了通过属性比较许多任意动态对象所需的基本工具。

//using System.Dynamic;
//using Dynamitey;
//using System.Linq;

IEnumerable<string> list1 =Dynamic.GetMemberNames(obj1);
list1 = list1.OrderBy(m=>m);
IEnumerable<string> list2 =Dynamic.GetMemberNames(obj2);
list2 = list2.OrderBy(m=>m);

if(!list1.SequenceEqual(list2))
 return false;

foreach(var memberName in list1){
 if(!Dynamic.InvokeGet(obj1, memberName).Equals(Dynamic.InvokeGet(obj2,memberName))){
    return false;
 }
}
return true;

但是,如果它们只是您自己的 DynamicObject 子类,那么遵循典型的 实现 Equals 的规则,与非动态对象确实没有区别,只需比较您内部使用的状态即可。

The Microsoft API's for dynamically invoking methods and propertys on arbitrary dynamic objects (IDynamicMetaObjectProvider) are not easy to use when you don't have the compiler's help. You can use Dynamitey (via nuget) to simplify this completely. It has a static function Dynamic.InvokeGet to call property's getters with just a target and a property name.

To get a list of properties of the dynamic object, there is a bit of a gotcha, as the dynamic object has to support it (if it's a DynamicObject that means implementing GetDynamicMemberNames, Expando supports it, but random IDynamicMetaObjectProvider may not and just return an empty list). Dynamitey has a method to simplifying getting those names as well, Dynamic.GetMemberNames.

Both of those two functions give you the basic tools necessary to compare many arbitrary dynamic objects via properties.

//using System.Dynamic;
//using Dynamitey;
//using System.Linq;

IEnumerable<string> list1 =Dynamic.GetMemberNames(obj1);
list1 = list1.OrderBy(m=>m);
IEnumerable<string> list2 =Dynamic.GetMemberNames(obj2);
list2 = list2.OrderBy(m=>m);

if(!list1.SequenceEqual(list2))
 return false;

foreach(var memberName in list1){
 if(!Dynamic.InvokeGet(obj1, memberName).Equals(Dynamic.InvokeGet(obj2,memberName))){
    return false;
 }
}
return true;

However, if they are just your own DynamicObject subclass then it'd be easier to just follow the typical rules for implementing Equals, there really is no difference from non-dynamic objects, and just compare what you are internally using for state.

尴尬癌患者 2024-12-08 11:45:24

ExpandoObject 实现 ICollection>(除了相同的 IDictionaryIEnumerable ),所以你应该能够很容易地比较它们的属性:

public static bool AreExpandosEquals(ExpandoObject obj1, ExpandoObject obj2)
{
    var obj1AsColl = (ICollection<KeyValuePair<string,object>>)obj1;
    var obj2AsDict = (IDictionary<string,object>)obj2;

    // Make sure they have the same number of properties
    if (obj1AsColl.Count != obj2AsDict.Count)
        return false;

    foreach (var pair in obj1AsColl)
    {
        // Try to get the same-named property from obj2
        object o;
        if (!obj2AsDict.TryGetValue(pair.Key, out o))
            return false;

        // Property names match, what about the values they store?
        if (!object.Equals(o, pair.Value))
            return false;
    }

    // Everything matches
    return true;
}

ExpandoObject implements ICollection<KeyValuePair<string, object>> (in addition to IDictionary and IEnumerable of the same), so you should be able to compare them property by property pretty easily:

public static bool AreExpandosEquals(ExpandoObject obj1, ExpandoObject obj2)
{
    var obj1AsColl = (ICollection<KeyValuePair<string,object>>)obj1;
    var obj2AsDict = (IDictionary<string,object>)obj2;

    // Make sure they have the same number of properties
    if (obj1AsColl.Count != obj2AsDict.Count)
        return false;

    foreach (var pair in obj1AsColl)
    {
        // Try to get the same-named property from obj2
        object o;
        if (!obj2AsDict.TryGetValue(pair.Key, out o))
            return false;

        // Property names match, what about the values they store?
        if (!object.Equals(o, pair.Value))
            return false;
    }

    // Everything matches
    return true;
}
悲欢浪云 2024-12-08 11:45:24

请参阅“枚举和删除成员”以获取 ExpandoObject 的成员 http 不过,://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

任意动态对象似乎不会公开枚举器。

See "Enumerating and deleting members" to get the members of an ExpandoObject http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx

Arbitrary dynamic objects do not appear to expose enumerators, though.

短暂陪伴 2024-12-08 11:45:24

您必须实施 IComparable -界面。然后,您就可以使用 .NET/C# 中所需的适当函数来相互比较两个对象。

You have to implement IComparable-Interface. Then you have the appropriate functions needed from .NET/C# to compare two objects with each other.

森林迷了鹿 2024-12-08 11:45:24

您还可以使用 GitHub 上提供的 ObjectsComparer 库:
ObjectsComparer

这个库是一个对象到对象比较器,允许我们逐个成员递归地比较对象,并且为某些属性、字段或类型定义自定义比较规则。它支持枚举(数组、集合、列表)、多维数组、枚举、标志和动态对象(ExpandoObject、DynamicObject 和编译器生成的动态对象)。

转到 Valerii Tereshchenko 优秀的论文提供了更多细节。

You can also use the ObjectsComparer library available on GitHub :
ObjectsComparer

This library is an object-to-object comparer that allows us to compare objects recursively member by member and to define custom comparison rules for certain properties, fields or types. It supports enumerables (arrays, collections, lists), multidimensional arrays, enumerations, flags and dynamic objects (ExpandoObject, DynamicObject and compiler generated dynamic objects).

Go to Valerii Tereshchenko excellent paper for more details.

☆獨立☆ 2024-12-08 11:45:24

Expando 对象可用作 IDictonary 因此您应该能够使用它。

Assert.AreEqual((IDictonary(object, string))obj1, (IDictonary(object, string))obj2); 

编辑 AreEqual 这样的东西将不起作用。

但您可以尝试相当简单地比较这两个词典。

Expando Objects are useable as an IDictonary<string, object> so you should be able to use that.

Something like

Assert.AreEqual((IDictonary(object, string))obj1, (IDictonary(object, string))obj2); 

Edit the AreEqual won't work.

But you could try comparing the two dictionaries fairly simply.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文