Làm cách nào để phát hiện nếu một tài sản tồn tại trên ExpandoObject?


188

Trong javascript, bạn có thể phát hiện nếu một thuộc tính được xác định bằng cách sử dụng từ khóa không xác định:

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

Làm thế nào bạn sẽ làm điều này trong C # bằng cách sử dụng từ khóa động có ExpandoObjectvà không có ngoại lệ?


5
@CodeInChaos: Lưu ý rằng mã được hiển thị không kiểm tra giá trị của data.myProperty; nó kiểm tra những gì typeof data.myPropertytrả về. Đó là chính xác data.myPropertycó thể tồn tại và được đặt thành undefined, nhưng trong trường hợp đó, typeofsẽ trả lại một cái gì đó khác hơn "undefined". Vì vậy, mã này không hoạt động.
Aasmund Eldhuset

Câu trả lời:


181

Theo MSDN , tuyên bố cho thấy nó đang thực hiện IDadata:

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

Bạn có thể sử dụng điều này để xem nếu một thành viên được xác định:

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

3
Để làm cho kiểm tra này đơn giản hơn, tôi đã quá tải TryGetValue và làm cho nó luôn trả về giá trị true, đặt giá trị trả về thành "không xác định" nếu thuộc tính không được xác định. if (someObject.someParam! = "không xác định") ... Và nó hoạt động :)
Softlion

Cũng có thể :), nhưng tôi nghĩ bạn có nghĩa là "bị ghi đè" thay vì quá tải.
Dykam

Vâng. Tôi lại thay đổi "không xác định" thành giá trị const của một đối tượng đặc biệt mà tôi đã tạo ở nơi khác. Nó ngăn các sự cố truyền: p
Softlion

1
Tôi tin rằng giải pháp này vẫn còn hiện hành; đừng lấy từ của ai về giá của sự phản ánh - hãy tự mình kiểm tra và xem bạn có đủ khả năng không
nik.shornikov

1
@ BlueRaja-DannyPflughoeft Vâng, đúng vậy. Không có dàn diễn viên, nó sẽ chỉ là một lời mời năng động, với trường hợp bạn nhận được trong nội bộ. Cụ thể hơn, nó được triển khai một cách rõ ràng: github.com/mono/mono/blob/master/mcs/
class / dlr / R nb / Giả

28

Một sự phân biệt quan trọng cần phải được thực hiện ở đây.

Hầu hết các câu trả lời ở đây là cụ thể cho ExpandoObject được đề cập trong câu hỏi. Nhưng một cách sử dụng phổ biến (và lý do để đáp ứng câu hỏi này khi tìm kiếm) là khi sử dụng ASP.Net MVC ViewBag. Đó là một triển khai / lớp con tùy chỉnh của DynamicObject, sẽ không ném Ngoại lệ khi bạn kiểm tra bất kỳ tên thuộc tính tùy ý nào cho null. Giả sử bạn có thể khai báo một tài sản như:

@{
    ViewBag.EnableThinger = true;
}

Sau đó, giả sử bạn muốn kiểm tra giá trị của nó và liệu nó có được đặt hay không - liệu nó có tồn tại hay không. Sau đây là hợp lệ, sẽ biên dịch, sẽ không đưa ra bất kỳ ngoại lệ nào và cung cấp cho bạn câu trả lời đúng:

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

Bây giờ hãy thoát khỏi tuyên bố của EnableThinger. Cùng mã biên dịch và chạy đúng. Không cần suy tư.

Không giống như ViewBag, ExpandoObject sẽ ném nếu bạn kiểm tra null trên một thuộc tính không tồn tại. Để có được chức năng nhẹ nhàng hơn của MVC ViewBag khỏi các dynamicđối tượng của bạn , bạn sẽ cần sử dụng một triển khai động mà không ném.

Bạn chỉ có thể sử dụng triển khai chính xác trong 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/DocateViewDataDipedia.cs

Bạn có thể thấy nó được gắn vào MVC Views ở đây, trong MVC ViewPage:

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

Chìa khóa cho hành vi duyên dáng của DynamicViewDataDipedia là việc triển khai Từ điển trên ViewDataDipedia, tại đây:

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/ViewDataDipedia.cs

Nói cách khác, nó luôn trả về một giá trị cho tất cả các khóa, bất kể cái gì trong đó - nó chỉ trả về null khi không có gì ở đó. Tuy nhiên, ViewDataDipedia có trách nhiệm gắn liền với Mô hình của MVC, vì vậy tốt hơn hết là chỉ loại bỏ các phần từ điển duyên dáng để sử dụng bên ngoài Chế độ xem MVC.

Quá lâu để thực sự đăng tất cả can đảm ở đây - hầu hết chỉ thực hiện IDadata - nhưng đây là một đối tượng động (lớp DDict) không đưa ra kiểm tra null cho các thuộc tính chưa được khai báo, trên Github:

https://github.com/b9chris/GracefulDocateDixi

Nếu bạn chỉ muốn thêm nó vào dự án của mình thông qua NuGet, tên của nó là GracefulDocateDipedia .


Tại sao bạn lại bỏ phiếu trên DynamicDipedia vì nó không sử dụng sự phản chiếu sau đó?
Softlion

sau đó bạn có thể bỏ phiếu vì đây là giải pháp tương tự :)
Softlion

3
Nó chắc chắn không phải là cùng một giải pháp.
Chris Moschini

"bạn sẽ cần tạo một lớp con tương tự không ném khi không tìm thấy thuộc tính." => nó là vậy! Ồ không, không. Giải pháp của tôi là tốt hơn. Nó ném - bởi vì chúng tôi muốn nó, VÀ nó cũng không thể ném nếu sử dụng TryXX;
Softlion

1
NÀY, ĐÂY chính xác là lý do tại sao tôi ở đây, tôi không thể hiểu tại sao một số mã (viewbag) KHÔNG bị phá vỡ. Cảm ơn bạn.
Adam Tolley

11

Tôi đã trả lời một câu hỏi rất giống nhau gần đây: Làm thế nào để tôi phản ánh về các thành viên của đối tượng động?

Một thời gian ngắn, ExpandoObject không phải là đối tượng động duy nhất bạn có thể nhận được. Sự phản chiếu sẽ hoạt động đối với các loại tĩnh (các loại không triển khai ID ẩnMetaObjectProvider). Đối với các loại thực hiện giao diện này, sự phản chiếu về cơ bản là vô dụng. Đối với ExpandoObject, bạn chỉ cần kiểm tra xem thuộc tính có được xác định là khóa trong từ điển cơ bản hay không. Đối với các triển khai khác, nó có thể là thách thức và đôi khi cách duy nhất là làm việc với các ngoại lệ. Để biết chi tiết, hãy theo liên kết ở trên.


11

CẬP NHẬT: Bạn có thể sử dụng các đại biểu và cố gắng lấy một giá trị từ thuộc tính đối tượng động nếu nó tồn tại. Nếu không có tài sản, chỉ cần bắt ngoại lệ và trả về false.

Hãy xem, nó hoạt động tốt cho tôi:

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

Đầu ra của mã là như sau:

True
True
False

2
@marklam Thật tệ khi bắt tất cả ngoại lệ khi bạn không biết nguyên nhân gây ra ngoại lệ. Trong trường hợp của chúng tôi, nó ổn vì chúng tôi hy vọng có thể không có trường.
Alexander G

3
nếu bạn biết nguyên nhân gây ra ngoại lệ, bạn cũng phải biết loại của nó, vì vậy hãy nắm bắt (AnyException) Nếu không, mã của bạn sẽ âm thầm tiếp tục ngay cả khi bạn có một ngoại lệ không mong muốn - ví dụ như OutOfMemoryException.
marklam

4
Bạn có thể vượt qua trong bất kỳ getter để IsPropertyExist. Trong ví dụ này, bạn biết người ta có thể ném một InvalidOperationException. Trong thực tế, bạn không biết ngoại lệ nào có thể bị ném. +1 để chống lại sự sùng bái hàng hóa.
piedar

2
Giải pháp này không được chấp nhận nếu hiệu suất là quan trọng, ví dụ: nếu được sử dụng trong một vòng lặp với hơn 500 lần lặp, nó cộng lại và có thể gây ra nhiều giây chậm trễ. Mỗi khi bắt gặp một ngoại lệ, ngăn xếp phải được sao chép vào đối tượng ngoại lệ
Jim109

1
Re: Performance: Trình gỡ lỗi được đính kèm và Console.WriteLine là các bit chậm. 10.000 lần lặp ở đây mất ít hơn 200ms (với 2 lần ngoại lệ cho mỗi lần lặp). Thử nghiệm tương tự không có ngoại lệ mất một vài phần nghìn giây. Điều đó có nghĩa là nếu bạn cho rằng việc sử dụng mã này của bạn hiếm khi bị thiếu một tài sản hoặc nếu bạn gọi nó là số lần giới hạn hoặc có thể lưu trữ kết quả, thì vui lòng nhận ra rằng mọi thứ đều có vị trí của nó và không có gì quá mức cảnh báo -regurgurg ở đây vấn đề.
Hỗn loạn

10

Tôi muốn tạo một phương thức mở rộng để tôi có thể làm một cái gì đó như:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

... nhưng bạn không thể tạo tiện ích mở rộng ExpandoObjecttheo tài liệu C # 5 (thông tin thêm ở đây ).

Vì vậy, tôi đã kết thúc việc tạo ra một người trợ giúp lớp:

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

Để dùng nó:

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

4
bỏ phiếu cho nhận xét hữu ích và liên kết trên các tiện ích mở rộng cho ExpandoObject.
Roberto

1

Tại sao bạn không muốn sử dụng Reflection để có được tập hợp các loại phù hợp? Như thế này

 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
 } 

Tôi không chắc liệu điều đó có trả về các thuộc tính được thêm động hay không, tôi đoán là nó sẽ trả về các phương thức của đối tượng Động.
Dykam

1

Phương thức mở rộng này kiểm tra sự tồn tại của một thuộc tính và sau đó trả về giá trị hoặc null. Điều này hữu ích nếu bạn không muốn các ứng dụng của mình đưa ra các ngoại lệ không cần thiết, ít nhất là các ứng dụng bạn có thể giúp đỡ.

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

Nếu có thể được sử dụng như vậy:

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

hoặc nếu biến cục bộ của bạn là 'động', trước tiên bạn sẽ phải chuyển nó sang ExpandoObject.

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

1

Tùy thuộc vào trường hợp sử dụng của bạn, nếu null có thể được coi là giống như không xác định, bạn có thể biến ExpandoObject của mình thành 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"));

Đầu ra:

a is 1
b is 2.5
c is undefined


-3

Này các bạn hãy ngừng sử dụng Reflection cho mọi thứ, nó tiêu tốn rất nhiều chu kỳ CPU.

Đây là giải pháp:

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

4
Điều này cho thấy cách thực hiện một đối tượng động, chứ không phải làm thế nào để nhìn thấy nó một thuộc tính thoát ra trên một đối tượng động.
Matt Warren

Bạn có thể kiểm tra xem một phiên bản động có thuộc tính hay không bằng cách thực hiện kiểm tra null đối với thuộc tính được đề cập.
ctorx

2
"Điều này cho thấy cách thực hiện một đối tượng động": thực tế nó là như vậy. Giải pháp cho câu hỏi này là: không có giải pháp chung vì nó phụ thuộc vào việc thực hiện.
Softlion

@Softlion Không, giải pháp là thứ mà chúng tôi phải ngừng sử dụng
nik.shornikov

@Softlion Điểm của các phương thức Tryxxx là gì? TryGet sẽ không bao giờ trả lại sai khi không tìm thấy thuộc tính, vì vậy bạn vẫn phải kiểm tra kết quả. Sự trở lại là vô ích. Trong TrySet, nếu khóa không tồn tại, thì nó sẽ đưa ra một ngoại lệ thay vì trả về false. Tôi không hiểu tại sao bạn thậm chí sẽ sử dụng nó như một câu trả lời, nếu chính bạn đã viết ở đây trên các bình luận "Giải pháp cho câu hỏi này là: không có giải pháp chung nào vì nó phụ thuộc vào việc thực hiện", điều đó cũng không đúng. Nhìn vào câu trả lời của Dykam cho giải pháp thực sự.
pqsk

-5

Hãy thử cái này

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

3
Điều này sẽ kiểm tra sự tồn tại của một thuộc tính của đối tượng ẩn dưới tên động, đó là một chi tiết triển khai. Bạn đã xác minh giải pháp của bạn trong mã thực trước khi đăng? Nó hoàn toàn không hoạt động.
Softlion

Tôi đã sử dụng đoạn mã này trong kịch bản thời gian thực. Nó hoạt động tốt.
Venkat

6
Cung cấp cho tôi null mọi lúc, ngay cả khi tài sản tồn tại.
atlantis

Nó sẽ hoạt động với các đối tượng cơ bản, nhưng không phải với ExpandoObjects. Động lực, không chắc chắn.
Joe

1
Để xác nhận, điều này cũng không hoạt động với dynamiccác đối tượng (luôn trả về null).
Mã hóa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.