c# - परीक्षण करें यदि एक गतिशील चर पर एक संपत्ति उपलब्ध है



dynamic dynamic-keyword (10)

डेनिस के जवाब ने मुझे जेसनऑब्जेक्ट्स का उपयोग करके एक और समाधान के बारे में सोचा,

एक हेडर संपत्ति परीक्षक:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).OfType<JProperty>()
                                     .Any(prop => prop.Name == "header");

या शायद बेहतर:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).Property("header") != null;

उदाहरण के लिए:

dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;

मेरी स्थिति बहुत सरल है। मेरे कोड में कहीं मेरे पास यह है:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

//How to do this?
if (myVariable.MyProperty.Exists)   
//Do stuff

तो, मूल रूप से मेरा सवाल यह है कि कैसे जांचें (अपवाद फेंकने के बिना) कि एक निश्चित संपत्ति मेरे गतिशील चर पर उपलब्ध है। मैं GetType() कर सकता GetType() लेकिन मैं इसके बजाय इससे GetType() क्योंकि मुझे वास्तव में वस्तु के प्रकार को जानने की आवश्यकता नहीं है। जो कुछ मैं वास्तव में जानना चाहता हूं वह यह है कि क्या एक संपत्ति (या विधि, यदि जीवन को आसान बनाता है) उपलब्ध है। कोई संकेतक?


मुझे लगता है कि यह पता लगाने का कोई तरीका नहीं है कि dynamic चर के पास एक निश्चित सदस्य है, इसे एक्सेस करने का प्रयास किए बिना, जब तक कि आप सी # कंपाइलर में गतिशील बाध्यकारी को नियंत्रित नहीं करते हैं। सी # विनिर्देश के अनुसार, जिसमें शायद अनुमान लगाया जा सकता है, क्योंकि यह क्रियान्वयन-परिभाषित है।

तो आपको वास्तव में सदस्य तक पहुंचने और अपवाद पकड़ने की कोशिश करनी चाहिए, अगर यह विफल हो जाती है:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 

मैंने सोचा कि मैं मार्टिजन के जवाब और svick के जवाब की तुलना करूंगा ...

निम्नलिखित कार्यक्रम निम्नलिखित परिणाम देता है:

Testing with exception: 2430985 ticks
Testing with reflection: 155570 ticks
void Main()
{
    var random = new Random(Environment.TickCount);

    dynamic test = new Test();

    var sw = new Stopwatch();

    sw.Start();

    for (int i = 0; i < 100000; i++)
    {
        TestWithException(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with exception: " + sw.ElapsedTicks.ToString() + " ticks");

    sw.Restart();

    for (int i = 0; i < 100000; i++)
    {
        TestWithReflection(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with reflection: " + sw.ElapsedTicks.ToString() + " ticks");
}

class Test
{
    public bool Exists { get { return true; } }
}

bool FlipCoin(Random random)
{
    return random.Next(2) == 0;
}

bool TestWithException(dynamic d, bool useExisting)
{
    try
    {
        bool result = useExisting ? d.Exists : d.DoesntExist;
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

bool TestWithReflection(dynamic d, bool useExisting)
{
    Type type = d.GetType();

    return type.GetProperties().Any(p => p.Name.Equals(useExisting ? "Exists" : "DoesntExist"));
}

नतीजतन मैं प्रतिबिंब का उपयोग करने का सुझाव देना होगा। निचे देखो।

ब्लेंड की टिप्पणी का जवाब:

अनुपात reflection:exception 100000 पुनरावृत्तियों के लिए reflection:exception टिक:

Fails 1/1: - 1:43 ticks
Fails 1/2: - 1:22 ticks
Fails 1/3: - 1:14 ticks
Fails 1/5: - 1:9 ticks
Fails 1/7: - 1:7 ticks
Fails 1/13: - 1:4 ticks
Fails 1/17: - 1:3 ticks
Fails 1/23: - 1:2 ticks
...
Fails 1/43: - 1:2 ticks
Fails 1/47: - 1:1 ticks

... काफी उचित - यदि आप उम्मीद करते हैं कि यह ~ 1/47 से कम की संभावना के साथ विफल हो जाए, तो अपवाद के लिए जाएं।

उपरोक्त मानते हैं कि आप हर बार GetProperties() चला रहे हैं। आप प्रत्येक प्रकार के शब्दकोश या इसी तरह के लिए GetProperties() के परिणाम को कैश करके प्रक्रिया को तेज करने में सक्षम हो सकते हैं। यह मदद कर सकता है अगर आप बार-बार प्रकार के उसी सेट के खिलाफ जांच कर रहे हैं।


मेरे लिए यह काम करता है:

if (IsProperty(() => DynamicObject.MyProperty))
  ; // do stuff



delegate string GetValueDelegate();

private bool IsProperty(GetValueDelegate getValueMethod)
{
    try
    {
        //we're not interesting in the return value.
        //What we need to know is whether an exception occurred or not

        var v = getValueMethod();
        return (v == null) ? false : true;
    }
    catch (RuntimeBinderException)
    {
        return false;
    }
    catch
    {
        return true;
    }
}

इसमें दो सामान्य समाधानों में कॉल करने और कॉल RuntimeBinderException प्रतिबिंब का उपयोग करके, या टेक्स्ट प्रारूप में क्रमबद्ध करने और वहां से पार्सिंग करने के लिए, RuntimeBinderException को पकड़ने और पकड़ने में RuntimeBinderException । अपवादों के साथ समस्या यह है कि वे बहुत धीमी हैं, क्योंकि जब कोई बनाया जाता है, तो वर्तमान कॉल स्टैक क्रमबद्ध होता है। जेएसओएन या कुछ समान रूप से सीरियलाइजिंग एक समान जुर्माना लगाता है। यह हमें प्रतिबिंब के साथ छोड़ देता है लेकिन यह केवल तभी काम करता है जब अंतर्निहित वस्तु वास्तव में वास्तविक सदस्यों के साथ एक पीओसीओ है। यदि यह एक शब्दकोश, एक COM ऑब्जेक्ट, या बाहरी वेब सेवा के चारों ओर एक गतिशील रैपर है, तो प्रतिबिंब मदद नहीं करेगा।

एक अन्य समाधान DynamicMetaObject का उपयोग सदस्य नाम प्राप्त करने के लिए करना है क्योंकि डीएलआर उन्हें देखता है। नीचे दिए गए उदाहरण में, मैं Age वर्ग के परीक्षण के लिए एक स्थिर वर्ग ( Dynamic ) का उपयोग करता Age और इसे प्रदर्शित करता Age

class Program
{
    static void Main()
    {
        dynamic x = new ExpandoObject();

        x.Name = "Damian Powell";
        x.Age = "21 (probably)";

        if (Dynamic.HasMember(x, "Age"))
        {
            Console.WriteLine("Age={0}", x.Age);
        }
    }
}

public static class Dynamic
{
    public static bool HasMember(object dynObj, string memberName)
    {
        return GetMemberNames(dynObj).Contains(memberName);
    }

    public static IEnumerable<string> GetMemberNames(object dynObj)
    {
        var metaObjProvider = dynObj as IDynamicMetaObjectProvider;

        if (null == metaObjProvider) throw new InvalidOperationException(
            "The supplied object must be a dynamic object " +
            "(i.e. it must implement IDynamicMetaObjectProvider)"
        );

        var metaObj = metaObjProvider.GetMetaObject(
            Expression.Constant(metaObjProvider)
        );

        var memberNames = metaObj.GetDynamicMemberNames();

        return memberNames;
    }
}

यदि आप गतिशील के रूप में उपयोग किए जाने वाले प्रकार को नियंत्रित करते हैं, तो क्या आप प्रत्येक संपत्ति के उपयोग के लिए मूल्य के बजाय एक tuple वापस नहीं कर सकते? कुछ इस तरह...

public class DynamicValue<T>
{
    internal DynamicValue(T value, bool exists)
    {
         Value = value;
         Exists = exists;
    }

    T Value { get; private set; }
    bool Exists { get; private set; }
}

संभवतः एक निष्पक्ष कार्यान्वयन, लेकिन यदि आप प्रत्येक बार आंतरिक रूप से इनमें से किसी एक का निर्माण करते हैं और वास्तविक मान के बजाय इसे वापस लौटते हैं, तो आप प्रत्येक संपत्ति के उपयोग पर Exists जांच कर सकते हैं और उसके बाद Value हिट कर सकते हैं यदि यह मान default(T) (और अप्रासंगिक) है अगर ऐसा नहीं होता है।

उस ने कहा, मुझे कुछ ज्ञान याद आ रहा है कि कैसे गतिशील काम करता है और यह एक व्यावहारिक सुझाव नहीं हो सकता है।


शायद प्रतिबिंब का उपयोग करें?

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any(); 

@karask द्वारा दिए गए उत्तर के बाद, आप फ़ंक्शन को एक सहायक के रूप में लपेट सकते हैं:

public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}

बस अगर यह किसी की मदद करता है:

यदि विधि GetDataThatLooksVerySimilarButNotTheSame() एक ExpandoObject देता है ExpandoObject आप जांच करने से पहले एक IDictionary भी डाल सकते हैं।

dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}

लिंक के बिना इसे करने के रूप में आपने कहा:

public class Order : IComparable
{
    public DateTime OrderDate { get; set; }
    public int OrderId { get; set; }

    public int CompareTo(object obj)
    {
        Order orderToCompare = obj as Order;
        if (orderToCompare.OrderDate < OrderDate || orderToCompare.OrderId < OrderId)
        {
            return 1;
        }
        if (orderToCompare.OrderDate > OrderDate || orderToCompare.OrderId > OrderId)
        {
            return -1;
        }

        // The orders are equivalent.
        return 0;
    }
}

फिर ऑर्डर की अपनी सूची पर बस .sort () पर कॉल करें





c# dynamic dynamic-keyword