it-swarm.asia

اختبار ما إذا كانت خاصية متوفرة على متغير ديناميكي

وضعي بسيط جدا. في مكان ما في الكود ، لدي هذا:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

لذلك ، سؤالي بشكل أساسي هو كيفية التحقق (دون رمي استثناء) من أن خاصية معينة متاحة في المتغير الديناميكي الخاص بي. يمكنني القيام بـ GetType() ولكني أفضل تجنب ذلك نظرًا لأنني لست بحاجة حقًا إلى معرفة نوع الكائن. كل ما أريد حقاً معرفته هو ما إذا كانت خاصية (أو طريقة ، إذا كان ذلك يجعل الحياة أسهل) متاحة. أي مؤشرات؟

203
roundcrisis

أعتقد أنه لا توجد طريقة لمعرفة ما إذا كان متغير dynamic يحتوي على عضو معين دون محاولة الوصول إليه ، إلا إذا قمت بإعادة تطبيق الطريقة التي تتم بها معالجة الربط الديناميكي في برنامج التحويل البرمجي C #. وهو ما قد يتضمن الكثير من التخمين ، لأنه معرف بالتنفيذ ، وفقًا لمواصفات C #.

لذلك يجب عليك فعلاً محاولة الوصول إلى العضو والاطلاع على استثناء ، إذا فشل:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

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

اعتقدت أنني سأقوم بمقارنة إجابة Martijn و إجابة 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 ticks لـ 100000 تكرار:

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() لكل نوع في القاموس أو ما شابه ذلك. قد يساعد هذا إذا كنت تتحقق من نفس مجموعة الأنواع مرارًا وتكرارًا.

66
dav_i

ربما استخدام التفكير؟

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

فقط في حالة مساعدة شخص ما:

إذا كانت الطريقة GetDataThatLooksVerySimilarButNotTheSame() تُرجع ExpandoObject ، فيمكنك أيضًا التحويل إلى IDictionary قبل التدقيق.

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

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

حسنا ، واجهت مشكلة مماثلة ولكن في اختبارات الوحدة.

باستخدام SharpTestsEx ، يمكنك التحقق من وجود خاصية. أستخدم هذا الاختبار لوحدات التحكم الخاصة بي ، لأنه بما أن كائن JSON ديناميكي ، فيمكن لأي شخص تغيير الاسم ونسيان تغييره في javascript أو أي شيء ، لذلك فإن اختبار جميع الخصائص عند كتابة وحدة التحكم يجب أن يزيد من سلامتي.

مثال:

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

باستخدام هذا ، أقوم باختبار جميع الخصائص الموجودة باستخدام "Should (). NotThrow ()".

ربما تكون خارجة عن الموضوع ، ولكن يمكن أن تكون مفيدة لشخص ما.

7
Diego Santin

أجابتني إجابة دينيس على حل آخر باستخدام 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;
7
Charles HETIER

يشتمل الحلان الشائعان لهذا الإجراء على إجراء المكالمة والقبض على 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;
    }
}
7
Damian Powell

بالنسبة لي هذا يعمل:

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

متابعة من إجابةkarask ، يمكنك التفاف الوظيفة كمساعد مثل:

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

أعرف أن هذا منشور قديم جدًا ولكن هنا حل بسيط للعمل مع dynamic type في c#.

  1. يمكن استخدام انعكاس بسيط لتعداد الخصائص المباشرة
  2. أو يمكنك استخدام object extention method
  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
SimperT

في حالتي ، كنت بحاجة للتحقق من وجود طريقة باسم محدد ، لذلك استخدمت واجهة لذلك

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

أيضًا ، يمكن أن تحتوي الواجهات على أكثر من مجرد طرق:

يمكن أن تحتوي الواجهات على أساليب أو خصائص أو أحداث أو مفهرسات أو أي مجموعة من هذه الأنواع الأربعة من الأعضاء.

من: واجهات (C # Programming Guide)

أنيقة ولا حاجة إلى فخ الاستثناءات أو اللعب مع انعكاس ...

0
Fred Mauroy

إذا كنت تتحكم في النوع الذي يتم استخدامه كديناميكي ، ألا يمكنك إرجاع 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) (إذا كانت غير موجودة) 'ر.

بعد قولي هذا ، ربما أفتقد بعض المعرفة حول كيفية العمل الديناميكي وهذا قد لا يكون اقتراحًا عمليًا.

0
Shibumi