وضعي بسيط جدا. في مكان ما في الكود ، لدي هذا:
dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();
//How to do this?
if (myVariable.MyProperty.Exists)
//Do stuff
لذلك ، سؤالي بشكل أساسي هو كيفية التحقق (دون رمي استثناء) من أن خاصية معينة متاحة في المتغير الديناميكي الخاص بي. يمكنني القيام بـ GetType()
ولكني أفضل تجنب ذلك نظرًا لأنني لست بحاجة حقًا إلى معرفة نوع الكائن. كل ما أريد حقاً معرفته هو ما إذا كانت خاصية (أو طريقة ، إذا كان ذلك يجعل الحياة أسهل) متاحة. أي مؤشرات؟
أعتقد أنه لا توجد طريقة لمعرفة ما إذا كان متغير dynamic
يحتوي على عضو معين دون محاولة الوصول إليه ، إلا إذا قمت بإعادة تطبيق الطريقة التي تتم بها معالجة الربط الديناميكي في برنامج التحويل البرمجي C #. وهو ما قد يتضمن الكثير من التخمين ، لأنه معرف بالتنفيذ ، وفقًا لمواصفات C #.
لذلك يجب عليك فعلاً محاولة الوصول إلى العضو والاطلاع على استثناء ، إذا فشل:
dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();
try
{
var x = myVariable.MyProperty;
// do stuff with x
}
catch (RuntimeBinderException)
{
// MyProperty doesn't exist
}
اعتقدت أنني سأقوم بمقارنة إجابة 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()
لكل نوع في القاموس أو ما شابه ذلك. قد يساعد هذا إذا كنت تتحقق من نفس مجموعة الأنواع مرارًا وتكرارًا.
ربما استخدام التفكير؟
dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any();
فقط في حالة مساعدة شخص ما:
إذا كانت الطريقة GetDataThatLooksVerySimilarButNotTheSame()
تُرجع ExpandoObject
، فيمكنك أيضًا التحويل إلى IDictionary
قبل التدقيق.
dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";
if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
Console.WriteLine(test.foo);
}
حسنا ، واجهت مشكلة مماثلة ولكن في اختبارات الوحدة.
باستخدام 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 ()".
ربما تكون خارجة عن الموضوع ، ولكن يمكن أن تكون مفيدة لشخص ما.
أجابتني إجابة دينيس على حل آخر باستخدام 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;
يشتمل الحلان الشائعان لهذا الإجراء على إجراء المكالمة والقبض على 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;
}
}
بالنسبة لي هذا يعمل:
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;
}
}
متابعة من إجابةkarask ، يمكنك التفاف الوظيفة كمساعد مثل:
public static bool HasProperty(ExpandoObject expandoObj,
string name)
{
return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}
أعرف أن هذا منشور قديم جدًا ولكن هنا حل بسيط للعمل مع dynamic
type في c#
.
- يمكن استخدام انعكاس بسيط لتعداد الخصائص المباشرة
- أو يمكنك استخدام
object
extention method- أو استخدم طريقة
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 );
}
}
في حالتي ، كنت بحاجة للتحقق من وجود طريقة باسم محدد ، لذلك استخدمت واجهة لذلك
var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
plugin.CustomPluginAction(action);
}
أيضًا ، يمكن أن تحتوي الواجهات على أكثر من مجرد طرق:
يمكن أن تحتوي الواجهات على أساليب أو خصائص أو أحداث أو مفهرسات أو أي مجموعة من هذه الأنواع الأربعة من الأعضاء.
من: واجهات (C # Programming Guide)
أنيقة ولا حاجة إلى فخ الاستثناءات أو اللعب مع انعكاس ...
إذا كنت تتحكم في النوع الذي يتم استخدامه كديناميكي ، ألا يمكنك إرجاع 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)
(إذا كانت غير موجودة) 'ر.
بعد قولي هذا ، ربما أفتقد بعض المعرفة حول كيفية العمل الديناميكي وهذا قد لا يكون اقتراحًا عمليًا.