it-swarm.asia

ما هو النوع "الديناميكي" في الإصدار C # 4.0 المستخدم؟

قدم C # 4.0 نوع جديد يسمى "ديناميكي". كل هذا يبدو جيدًا ، لكن ما الذي يمكن للمبرمج استخدامه؟

هل هناك موقف حيث يمكن أن ينقذ اليوم؟

201
Fahad

الكلمة الأساسية الديناميكية جديدة على C # 4.0 ، ويتم استخدامها لإعلام المترجم بأن نوع المتغير يمكن أن يتغير أو أنه غير معروف حتى وقت التشغيل. فكر في الأمر على أنه قادر على التفاعل مع كائن دون الحاجة إلى إلقاءه.

dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!

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

يوضح المثال أعلاه كيف تعمل ديناميكية عند استدعاء الأساليب والخصائص. ميزة أخرى قوية (وربما خطيرة) هي القدرة على إعادة استخدام المتغيرات لأنواع مختلفة من البيانات. أنا متأكد من أنه يمكن لمبرمجي Python و Ruby و Perl التفكير في مليون طريقة للاستفادة من ذلك ، لكنني أستخدم C # لفترة طويلة لدرجة أنه يبدو لي "خطأ" بالنسبة لي.

dynamic foo = 123;
foo = "bar";

حسنًا ، لذلك على الأرجح لن تكتب رمزًا مثل ما ورد أعلاه كثيرًا. ومع ذلك ، قد تكون هناك أوقات ، عندما تكون إعادة الاستخدام المتغيرة في متناول يدي أو تنظيف جزء قديم من التعليمات البرمجية القديمة. إحدى الحالات البسيطة التي أواجهها في كثير من الأحيان هي الاضطرار إلى الإدلاء باستمرار بين عشري ومضاعف.

decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");

لا يتم ترجمة السطر الثاني لأنه يتم كتابة 2.5 كـ مزدوج ولا يتم ترجمة السطر 3 لأن Math.Sqrt تتوقع مزدوج. من الواضح أن كل ما عليك فعله هو اختيار نوع متغير و/أو تغييره ، ولكن قد تكون هناك مواقف يكون فيها الديناميكي منطقيًا.

dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");

اقرأ المزيد من الميزات: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

161
Pranay Rana

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

خذ مثالا.

إذا كان لديك كائن COM ، مثل كائن Word.Application ، وتريد فتح مستند ، فإن طريقة القيام بذلك تأتي مع ما لا يقل عن 15 معلمة ، معظمها اختياري.

للاتصال بهذه الطريقة ، قد تحتاج إلى شيء مثل هذا (أنا أبسط ، هذا ليس رمزًا فعليًا):

object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing);

لاحظ كل هذه الحجج؟ تحتاج إلى تمرير تلك منذ C # قبل الإصدار 4.0 لم يكن لديك فكرة عن الوسائط الاختيارية. في C # 4.0 ، أصبحت واجهات برمجة تطبيقات COM أسهل في العمل من خلال تقديم:

  1. الحجج الاختيارية
  2. جعل ref اختياريًا لواجهة برمجة تطبيقات COM
  3. الحجج المسماة

بناء الجملة الجديد للاتصال أعلاه سيكون:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

ترى كيف تبدو أسهل بكثير ، وكيف يصبح أكثر قابلية للقراءة؟

لنفصل ذلك:

                                    named argument, can skip the rest
                                                   |
                                                   v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
                                 ^                         ^
                                 |                         |
                               notice no ref keyword, can pass
                               actual parameter values instead

والسحر هو أن برنامج التحويل البرمجي C # سيضخ الآن الشفرة اللازمة ، ويعمل مع فصول جديدة في وقت التشغيل ، لفعل نفس الشيء بالضبط الذي فعلته من قبل ، ولكن تم إخفاء بناء الجملة عنك ، والآن يمكنك التركيز على ماذا ، وليس كثيرا على كيف . يعجب Anders Hejlsberg بالقول إن عليك استدعاء "تعويذات" مختلفة ، وهو نوع من التورية السحرية لكل شيء ، حيث عادةً ما تضطر إلى توجيه يدك (ق) ولفظ بعض الكلمات السحرية بالترتيب الصحيح للحصول على نوع معين من الإملاء مستمرة. طريقة API القديمة للتحدث إلى كائنات COM كانت الكثير من ذلك ، كنت بحاجة للقفز من خلال الكثير من الأطواق من أجل اقناع المترجم البرمجي لترجمة الشفرة لك.

تنقسم الأشياء في C # قبل الإصدار 4.0 أكثر من ذلك إذا حاولت التحدث إلى كائن COM ليس لديك واجهة أو فئة له ، كل ما لديك مرجع IDispatch.

إذا كنت لا تعرف ما هو ، IDispatch بشكل أساسي انعكاس لكائنات COM. من خلال واجهة IDispatch ، يمكنك أن تطلب من الكائن "ما هو رقم المعرف للطريقة المعروفة باسم حفظ" ، وبناء صفائف من نوع معين يحتوي على قيم الوسيطة ، وأخيرا استدعاء طريقة Invoke على واجهة IDispatch لاستدعاء الطريقة ، تمرير جميع المعلومات التي تمكنت من استمالة معا.

قد تبدو طريقة الحفظ أعلاه بهذه الطريقة (هذا بالتأكيد ليس الرمز الصحيح):

string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);

كل هذا لمجرد فتح وثيقة.

كان لدى VB وسيطات اختيارية ودعم لمعظم هذا خارج الصندوق منذ فترة طويلة ، لذلك رمز C #:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

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

يمكنك العثور على مزيد من المعلومات حول واجهة IDispatch على ويكيبيديا: IDispatch إذا كنت ترغب في قراءة المزيد عنها. انها حقا الأشياء السخيفة.

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

ولروبي؟ واجهة برمجة تطبيقات مختلفة لا تزال.

جافا سكريبت؟ نفس الصفقة ، API مختلفة لذلك أيضا.

تتكون الكلمة الأساسية الديناميكية من شيئين:

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

غير أن الكلمة الأساسية dynamic لا تهدف إلى استبدال أي رمز .NET فقط موجود. بالتأكيد ، أنت يمكن تفعل ذلك ، لكن لم تتم إضافته لهذا السبب ، وكان مؤلفو لغة البرمجة C # مع Anders Hejlsberg في المقدمة ، مصممين بشدة على أنهم ما زالوا يعتبرون C # مطبوعًا بشدة اللغة ، ولن تضحي بهذا المبدأ.

هذا يعني أنه على الرغم من أنه يمكنك كتابة رمز مثل هذا:

dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;

واحصل على ترجمة ، لم يكن المقصود منه نوع من أنواع أنظمة وقت التشغيل السحرية التي تتيح لك معرفة ما الذي تريده.

كان الغرض كله هو تسهيل التحدث مع أنواع أخرى من الكائنات.

هناك الكثير من المواد على الإنترنت حول الكلمة الرئيسية ، المؤيدين ، المعارضين ، المناقشات ، التشدقات ، الثناء ، إلخ.

أقترح عليك البدء بالارتباطات التالية ثم google للمزيد:

194
Lasse Vågsæther Karlsen

أنا مندهش من أن لا أحد ذكر إرسال متعددة . الطريقة المعتادة للتغلب على هذا هي عبر نمط الزائر وهذا غير ممكن دائمًا حتى ينتهي بك الأمر مع عمليات فحص مكشوفة is.

حتى هنا هو مثال على الحياة الحقيقية لتطبيق بلدي. بدلا من القيام:

public static MapDtoBase CreateDto(ChartItem item)
{
    if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
    if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
    if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
    //other subtypes follow
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

أنت تفعل:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

private static MapDtoBase CreateDtoImpl(ChartItem item)
{
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

private static MapDtoBase CreateDtoImpl(MapPoint item)
{
    return new MapPointDto(item);
}

private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
    return new ElevationDto(item);
}

لاحظ أنه في الحالة الأولى ElevationPoint هي فئة فرعية من MapPoint وإذا لم يتم وضعها قبل / MapPointit فلن يتم الوصول إليها أبدًا. ليست هذه هي الحالة الديناميكية ، حيث سيتم استدعاء أقرب طريقة مطابقة.

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

21
Stelios Adamantidis

يسهل على اللغات المكتوبة الثابتة (CLR) التعامل مع اللغات الديناميكية (بيثون ، روبي ...) التي تعمل على DLR (وقت تشغيل اللغة الديناميكي) ، راجع MSDN :

على سبيل المثال ، يمكنك استخدام الكود التالي لزيادة العداد في XML في C #.

Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);

باستخدام DLR ، يمكنك استخدام الكود التالي بدلاً من ذلك لنفس العملية.

scriptobj.Count += 1;

تسرد MSDN هذه الميزات:

  • يبسط نقل اللغات الديناميكية إلى .NET Framework
  • تمكين الميزات الديناميكية بلغات مطبوعة بشكل ثابت
  • يوفر فوائد مستقبلية لـ DLR و .NET Framework
  • يتيح مشاركة المكتبات والكائنات
  • يوفر الإرسال الديناميكي السريع والاستدعاء

انظر MSDN لمزيد من التفاصيل.

11
Philip Daubmeier

مثال على الاستخدام:

تستهلك العديد من الفئات التي لها خاصية مشتركة 'CreationDate':

public class Contact
{
    // some properties

    public DateTime CreationDate { get; set; }        
}

public class Company
{
    // some properties

    public DateTime CreationDate { get; set; }

}

public class Opportunity
{
    // some properties

    public DateTime CreationDate { get; set; }

}

إذا كتبت طريقة مشتركة تسترد قيمة خاصية "CreationDate" ، فيجب عليك استخدام الانعكاس:

    static DateTime RetrieveValueOfCreationDate(Object item)
    {
        return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
    }

من خلال المفهوم "الديناميكي" ، أصبح الكود أكثر أناقة:

    static DateTime RetrieveValueOfCreationDate(dynamic item)
    {
        return item.CreationDate;
    }
4
Akli

COM interop. خاصة أنا غير معروف. تم تصميمه خصيصا لذلك.

3
Aen Sidhe

كانت أفضل حالة استخدام لمتغيرات النوع "الديناميكي" بالنسبة لي عندما كنت في الآونة الأخيرة ، أكتب طبقة وصول إلى البيانات في ADO.NET ( باستخدام SQLDataReader ) وكان الرمز يستدعي الإجراءات المخزنة القديمة المكتوبة بالفعل. هناك المئات من تلك الإجراءات المخزنة القديمة التي تحتوي على الجزء الأكبر من منطق العمل. طبقة الوصول إلى البيانات الخاصة بي ضرورية لإرجاع نوع من البيانات المهيكلة إلى طبقة منطق الأعمال ، القائمة على C # ، للقيام ببعض المعالجات ( على الرغم من أنه لا يوجد تقريبًا ). إرجاع كل الإجراءات المخزنة مجموعة مختلفة من البيانات ( أعمدة الجدول ). لذا بدلاً من إنشاء العشرات من الفئات أو البنيات للاحتفاظ بالبيانات التي تم إرجاعها ونقلها إلى BLL ، كتبت الرمز أدناه الذي يبدو أنيقًا وأنيقًا.

public static dynamic GetSomeData(ParameterDTO dto)
        {
            dynamic result = null;
            string SPName = "a_legacy_stored_procedure";
            using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
            {
                SqlCommand command = new SqlCommand(SPName, connection);
                command.CommandType = System.Data.CommandType.StoredProcedure;                
                command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
                command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
                connection.Open();
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        dynamic row = new ExpandoObject();
                        row.EmpName = reader["EmpFullName"].ToString();
                        row.DeptName = reader["DeptName"].ToString();
                        row.AnotherColumn = reader["AnotherColumn"].ToString();                        
                        result = row;
                    }
                }
            }
            return result;
        }
1
user1451111

سيتم استخدامه في الغالب من قبل RAD وضحايا بايثون لتدمير جودة الرمز ، التحسس الذكي وتجميع اكتشاف أخطاء الوقت.

1
stormianrootsolver

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

public class A
{
    // attributes and constructor here
    public virtual dynamic Clone()
    {
        var clone = new A();
        // Do more cloning stuff here
        return clone;
    }
}

public class B : A
{
    // more attributes and constructor here
    public override dynamic Clone()
    {
        var clone = new B();    
        // Do more cloning stuff here
        return clone;
    }
}    

public class Program
{
    public static void Main()
    {
        A a = new A().Clone();  // No cast needed here
        B b = new B().Clone();  // and here
        // do more stuff with a and b
    }
}
0
Frederic
  1. يمكنك الاتصال بلغات ديناميكية مثل CPython باستخدام pythonnet:

dynamic np = Py.Import("numpy")

  1. يمكنك إرسال عناصر عامة إلى dynamic عند تطبيق العوامل العددية عليها. وهذا يوفر سلامة النوع ويتجنب القيود المفروضة على الأدوية. هذا في جوهره * كتابة البط:

T y = x * (dynamic)x ، حيث typeof(x) is T

0
denfromufa