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


225

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

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

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


1
यहाँ कुछ सुझाव दिए गए हैं: stackoverflow.com/questions/2985161/… - लेकिन अब तक कोई स्वीकृत उत्तर नहीं है।
एंड्रयू एंडरसन

धन्यवाद, मैं देख सकता हूं कि कैसे एफआईआर को एक समाधान बनाया जाए, मैं सोच रहा था कि क्या कुछ है जो मुझे याद आ रहा है
राउंडक्रिस

जवाबों:


159

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

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

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

2
मैं इसे उत्तर के रूप में चिह्नित करूंगा क्योंकि यह बहुत लंबा है, यह सबसे अच्छा उत्तर प्रतीत होता है
राउंडक्रिस

8
बेहतर समाधान - stackoverflow.com/questions/2839598/...
ministrymason

20
@ministrymason यदि आप का मतलब है IDictionaryकि उसके साथ काम करना और उसके साथ काम करना, वह केवल उसी पर काम करता है ExpandoObject, तो वह किसी अन्य dynamicवस्तु पर काम नहीं करेगा ।
१६:

5
RuntimeBinderExceptionमें है Microsoft.CSharp.RuntimeBinderनाम स्थान।
डेविड आरआर

8
मुझे अभी भी ऐसा लगता है कि इस परिदृश्य की बारीकियों की परवाह किए बिना सामान्य रूप से बुरा व्यवहार है या नहीं, इसके बजाय कोशिश / पकड़ का उपयोग करें।
अलेक्जेंडर रयान

74

मुझे लगा कि मैं मार्टिज़न के जवाब और स्विक के उत्तर की तुलना करूँगा ...

निम्न प्रोग्राम निम्न परिणाम देता है:

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:exception100000 पुनरावृत्तियों के लिए टिक हैं :

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()एक शब्दकोश या समान में प्रत्येक प्रकार के परिणाम को कैशिंग करके प्रक्रिया को गति देने में सक्षम हो सकते हैं । यदि आप बार-बार एक ही प्रकार के सेट के खिलाफ जाँच कर रहे हैं तो इससे मदद मिल सकती है।


7
मैं सहमत हूं और अपने काम में प्रतिबिंब को पसंद करता हूं जहां उपयुक्त हो। ट्राय / कैच से जो लाभ हुआ है वह पूरी तरह से अपवाद को फेंकने पर है। तो किसी को यहां प्रतिबिंब का उपयोग करने से पहले क्या पूछना चाहिए - क्या यह एक निश्चित तरीका है? 90% या 75% समय, क्या आपका कोड पास होगा? फिर कोशिश / पकड़ अभी भी इष्टतम है। यदि हवा में इसका उठना, या किसी एक के लिए बहुत अधिक विकल्प होने की संभावना है, तो आपका प्रतिबिंब हाजिर है।
नरम

@bland संपादित उत्तर।
dav_i

1
धन्यवाद, अब वास्तव में पूर्ण दिखता है।
नरम

@dav_i दोनों की तुलना करना उचित नहीं है क्योंकि दोनों अलग-अलग व्यवहार करते हैं। svick का जवाब अधिक पूर्ण है।
नवफाल

1
@dav_i नहीं, वे एक ही कार्य नहीं करते हैं। मार्टिज़न का उत्तर यह जाँचता है कि क्या कोई संपत्ति C # में एक नियमित संकलन समय प्रकार पर मौजूद है, जिसे गतिशील घोषित किया गया है (जिसका अर्थ है कि यह संकलन सुरक्षा समय की अनदेखी करता है)। जबकि svick का उत्तर यह जाँचता है कि क्या कोई संपत्ति वास्तव में गतिशील वस्तु पर मौजूद है , अर्थात ऐसा कुछ जो लागू होता है IIDynamicMetaObjectProvider। मैं आपके उत्तर के पीछे की प्रेरणा को समझता हूं, और इसकी सराहना करता हूं। इसका जवाब देना उचित है।
नवाफल

52

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

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

2
प्रश्न से उद्धरण "। मैं GetType () कर सकता था, लेकिन मैं इससे बचना चाहता था"
राउंडक्रिस


12
आप बस इसके बिना कर सकते हैं Where:.Any(p => p.Name.Equals("PropertyName"))
dav_i

उत्तर तुलना के लिए कृपया मेरा उत्तर देखें
dav_i

3
एक-लाइनर के रूप में ((Type)myVar.GetType()).GetProperties().Any(x => x.Name.Equals("PropertyName")):। संकलक को लंबोदर के बारे में खुश करने के लिए टाइप टू कास्ट की आवश्यकता होती है।
MushinNoShin

38

बस मामले में यह किसी की मदद करता है:

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

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

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

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

7
@Wolfshead यह उत्तर बहुत अच्छा है यदि आप जानते हैं कि आपकी डायनामिक ऑब्जेक्ट एक ExpandoObject या कुछ और है जो IDEDIA को <string> object> को कार्यान्वित करता है, लेकिन यदि यह कुछ और होता है, तो यह विफल हो जाएगा।
डेमियन पॉवेल

9

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

एक अन्य उपाय यह है DynamicMetaObjectकि सदस्य नामों को प्राप्त करने के लिए उपयोग करें क्योंकि DLR उन्हें देखता है। नीचे दिए गए उदाहरण में, मैं फ़ील्ड Dynamicका परीक्षण करने 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;
    }
}

यह पता चला है कि Dynamiteyनगेट पैकेज पहले से ही ऐसा करता है। ( nuget.org/packages/Dynamitey )
डेमियन पॉवेल

8

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

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

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;

1
क्या यह जानने का मौका है कि कृपया इस उत्तर में क्या गलत है?
चार्ल्स HETIER

न जाने क्यों इस पर मतदान हुआ, मेरे लिए बहुत अच्छा काम किया। मैंने प्रत्येक संपत्ति के लिए सहायक को एक सहायक वर्ग में स्थानांतरित कर दिया और प्रत्येक से एक बूल वापस करने के लिए इनवोक विधि को बुलाया।
13

7

ठीक है, मुझे एक समान समस्या का सामना करना पड़ा लेकिन यूनिट परीक्षणों पर।

SharpTestsEx का उपयोग करके आप देख सकते हैं कि कोई संपत्ति मौजूद है। मैं इस परीक्षण को अपने नियंत्रकों का उपयोग करता हूं, क्योंकि चूंकि JSON ऑब्जेक्ट गतिशील है, इसलिए कोई व्यक्ति नाम बदल सकता है और इसे जावास्क्रिप्ट या किसी चीज़ में बदलना भूल सकता है, इसलिए नियंत्रक लिखते समय सभी गुणों के लिए परीक्षण करना मेरी सुरक्षा को बढ़ाना चाहिए।

उदाहरण:

dynamic testedObject = new ExpandoObject();
testedObject.MyName = "I am a testing object";

अब, SharTestsEx का उपयोग कर:

Executing.This(delegate {var unused = testedObject.MyName; }).Should().NotThrow();
Executing.This(delegate {var unused = testedObject.NotExistingProperty; }).Should().Throw();

इसका उपयोग करके, मैं "चाहिए ()। NotThrow ()" का उपयोग करके सभी मौजूदा गुणों का परीक्षण करता हूं।

यह शायद विषय से बाहर है, लेकिन किसी के लिए उपयोगी हो सकता है।


धन्यवाद, बहुत उपयोगी है। SharpTestsEx का उपयोग करते हुए मैं डायनेमिक प्रॉपर्टी के मूल्य का परीक्षण करने के लिए निम्न लाइन का उपयोग करता हूं:((string)(testedObject.MyName)).Should().Be("I am a testing object");
रेम्को जानसेन

2

@Kask द्वारा जवाब देने के बाद, आप फ़ंक्शन को सहायक की तरह लपेट सकते हैं:

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

2

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

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;
    }
    catch (RuntimeBinderException)
    {
        return false;
    }
    catch
    {
        return true;
    }
}

nullइसका मतलब यह नहीं है कि संपत्ति मौजूद नहीं है
quetzalcoatl

मुझे पता है, लेकिन अगर यह शून्य है, तो मुझे मूल्य के साथ कुछ भी करने की आवश्यकता नहीं है इसलिए मेरे usecase के लिए यह ठीक है
Jester

0

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

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)(और अप्रासंगिक) है तो नहीं।

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


0

मेरे मामले में, मुझे एक विशिष्ट नाम के साथ एक विधि के अस्तित्व की जांच करने की आवश्यकता थी, इसलिए मैंने उसके लिए एक इंटरफ़ेस का उपयोग किया

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}

इसके अलावा, इंटरफेस में केवल तरीकों से अधिक हो सकते हैं:

इंटरफेस में उन चार सदस्य प्रकारों के तरीके, गुण, घटनाएँ, अनुक्रमणिका या कोई संयोजन हो सकता है।

से: इंटरफेस (C # प्रोग्रामिंग गाइड)

सुरुचिपूर्ण और अपवादों के जाल में फंसने की जरूरत नहीं है या प्रतिपल ...


0

मुझे पता है कि यह वास्तव में पुरानी पोस्ट है लेकिन यहां dynamicटाइप में काम करने का एक सरल उपाय है c#

  1. प्रत्यक्ष गुणों की गणना करने के लिए सरल प्रतिबिंब का उपयोग कर सकते हैं
  2. या बाहर objectनिकालने की विधि का उपयोग कर सकते हैं
  3. या GetAsOrDefault<int>मौजूद नहीं होने पर डिफ़ॉल्ट रूप से मान के साथ एक नई दृढ़ता से टाइप की गई वस्तु प्राप्त करने के लिए विधि का उपयोग करें ।
public static class DynamicHelper
{
    private static void Test( )
    {
        dynamic myobj = new
                        {
                            myInt = 1,
                            myArray = new[ ]
                                      {
                                          1, 2.3
                                      },
                            myDict = new
                                     {
                                         myInt = 1
                                     }
                        };

        var myIntOrZero = myobj.GetAsOrDefault< int >( ( Func< int > )( ( ) => myobj.noExist ) );
        int? myNullableInt = GetAs< int >( myobj, ( Func< int > )( ( ) => myobj.myInt ) );

        if( default( int ) != myIntOrZero )
            Console.WriteLine( $"myInt: '{myIntOrZero}'" );

        if( default( int? ) != myNullableInt )
            Console.WriteLine( $"myInt: '{myNullableInt}'" );

        if( DoesPropertyExist( myobj, "myInt" ) )
            Console.WriteLine( $"myInt exists and it is: '{( int )myobj.myInt}'" );
    }

    public static bool DoesPropertyExist( dynamic dyn, string property )
    {
        var t = ( Type )dyn.GetType( );
        var props = t.GetProperties( );
        return props.Any( p => p.Name.Equals( property ) );
    }

    public static object GetAs< T >( dynamic obj, Func< T > lookup )
    {
        try
        {
            var val = lookup( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return null;
    }

    public static T GetAsOrDefault< T >( this object obj, Func< T > test )
    {
        try
        {
            var val = test( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return default( T );
    }
}

0

जैसा ExpandoObjectकि IDictionary<string, object>आपको विरासत में मिला है, आप निम्न चेक का उपयोग कर सकते हैं

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

if (((IDictionary<string, object>)myVariable).ContainsKey("MyProperty"))    
//Do stuff

आप इस चेक को करने के लिए एक यूटिलिटी मेथड बना सकते हैं, जिससे कोड बहुत ज्यादा क्लीनर और री-यूजेबल हो जाएगा


-1

यहाँ दूसरा तरीका है:

using Newtonsoft.Json.Linq;

internal class DymanicTest
{
    public static string Json = @"{
            ""AED"": 3.672825,
            ""AFN"": 56.982875,
            ""ALL"": 110.252599,
            ""AMD"": 408.222002,
            ""ANG"": 1.78704,
            ""AOA"": 98.192249,
            ""ARS"": 8.44469
}";

    public static void Run()
    {
        dynamic dynamicObject = JObject.Parse(Json);

        foreach (JProperty variable in dynamicObject)
        {
            if (variable.Name == "AMD")
            {
                var value = variable.Value;
            }
        }
    }
}

2
आपको यह विचार कहां से मिला कि यह सवाल जोबजेक्ट के गुणों के परीक्षण के बारे में है? आपका उत्तर उन वस्तुओं / वर्गों तक सीमित है जो उनके गुणों पर IEnumerable को उजागर करते हैं। की गारंटी नहीं dynamicdynamicकीवर्ड बहुत व्यापक विषय है। जाओ जांच करता है, तो आप के लिए परीक्षण कर सकते हैं Countमें dynamic foo = new List<int>{ 1,2,3,4 }जैसे कि
Quetzalcoatl
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.