سؤال

أرغب في جمع أكبر قدر ممكن من المعلومات فيما يتعلق بإصدار API في .NET / CLR، وتحديدا كيف تقوم API بتغييرات API أو لا تنقطع تطبيقات العميل. أولا، دعنا نحدد بعض المصطلحات:

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

كسر المستوى الثنائي - تغيير API الذي ينتج عنه جمعيات العميل التي تم تجميعها ضد الإصدار الأقدم من API يحتمل أن يتم تحميل الإصدار الجديد. مثال: تغيير طريقة توقيع الأسلوب، حتى لو كان يسمح بالدعوة بنفس الطريقة كما كان من قبل (أي: الفراغ لإرجاع القيم الافتراضية النوع / المعلمة الزائد).

استراحة مستوى المصدر - تغيير API الذي ينتج عنه التعليمات البرمجية الموجودة مكتوبة لتجميع الإصدار الأقدم من API يحتمل أن تتجمع إلى الإصدار الجديد. تعمل جمعيات العميل المجمعة بالفعل كما كان من قبل. مثال: إضافة حاملة جديدة يمكن أن يؤدي إلى الغموض في مكالمات الأسلوب التي كانت غير مبالغة سابقا.

تغيير مستوى الدلالات الهادئة على مستوى المصدر - تغيير API الذي ينتج عنه التعليمات البرمجية الموجودة مكتوبة لتجميع الإصدار الأقدم من API تغيير هادئ في دلالاتها، على سبيل المثال عن طريق استدعاء طريقة مختلفة. ومع ذلك، يجب أن يستمر الرمز في الترجمة مع عدم وجود تحذيرات / أخطاء، ويجب أن تعمل التجميعات المترجمة مسبقا كما كان من قبل. مثال: تنفيذ واجهة جديدة على فئة موجودة تؤدي إلى التحميل الزائد المختلفة يتم اختيارها أثناء دقة التحميل الزائد.

الهدف النهائي هو فوضوح العديد من التغييرات API من الدلالات العاجلة والهدوء قدر الإمكان، ووصف التأثير الدقيق للكسر، واللغات التي لا تتأثر بها. للتوسع في الأخير: في حين أن بعض التغييرات تؤثر على جميع اللغات عالميا (مثل إضافة عضو جديد إلى واجهة سوف تنكسر تطبيقات تلك الواجهة بأي لغة)، فإن البعض يتطلب من دلالات لغة محددة للغاية للدخول في اللعب للحصول على استراحة. هذا عادة ما ينطوي عادة على التحميل الزائد، وبشكل عام، أي شيء يتعلق بتحويلات الكتابة الضمنية. لا يبدو أن هناك بأي حال من الأحوال لتحديد "المقام الأقل شائعة" هنا حتى لغات CLS-مطابقة (أي تلك المطابقة على الأقل إلى قواعد "CLS للمستهلك" كما هو محدد في CLI Spec) - على الرغم من أنني سأكون ممتنا إذا شخص ما يصحمني على أنه خاطئ هنا - لذلك سيضطر هذا إلى الذهاب اللغة حسب اللغة. هؤلاء من معظم الاهتمام هم من الطبيعي تلك التي تأتي مع .net خارج الصندوق: C #، VB و F #؛ لكن آخرين، مثل IronPython، IronRuby، Delphi Prism وما علاقة أيضا. كلما زاد عدد الزاوية، كلما زاد إثارة إثارة للاهتمام - أشياء مثل إزالة الأعضاء بديهية للغاية، لكن التفاعلات الدقيقة بين مثل طريقة التحميل الزائد أو المعلمات الاختيارية / الافتراضية، يمكن أن يكون الاستدلال من نوع Lambda ومشغلي التحويل مفاجأة للغاية في بعض الأحيان.

بعض الأمثلة لتعبئة هذا:

إضافة طريقة جديدة الزائدة

النوع: استراحة مستوى المصدر

اللغات المتأثرة: C #، VB، F #

API قبل التغيير:

public class Foo
{
    public void Bar(IEnumerable x);
}

API بعد التغيير:

public class Foo
{
    public void Bar(IEnumerable x);
    public void Bar(ICloneable x);
}

عينة رمز العميل تعمل قبل التغيير وكسر بعد:

new Foo().Bar(new int[0]);

إضافة الحمل الزائد عامل التحويل الضمني الجديد

النوع: استراحة مستوى المصدر.

تتأثر اللغات: C #، VB

اللغات التي لا تتأثر: F #

API قبل التغيير:

public class Foo
{
    public static implicit operator int ();
}

API بعد التغيير:

public class Foo
{
    public static implicit operator int ();
    public static implicit operator float ();
}

عينة رمز العميل تعمل قبل التغيير وكسر بعد:

void Bar(int x);
void Bar(float x);
Bar(new Foo());

ملاحظات: F # غير مكسورة، لأنه ليس لديه أي دعم مستوى لغة للمشغلين الزائدين، ولا يتعين استدعاء كلاهما بشكل مباشر op_Explicit و op_Implicit طرق.

إضافة طرق مثيل جديدة

نوع: تغيير دلالات هادئة على مستوى المصدر.

تتأثر اللغات: C #، VB

اللغات التي لا تتأثر: F #

API قبل التغيير:

public class Foo
{
}

API بعد التغيير:

public class Foo
{
    public void Bar();
}

نموذج عميل العينة الذي يعاني من دلالات هادئة تتغير:

public static class FooExtensions
{
    public void Bar(this Foo foo);
}

new Foo().Bar();

ملاحظات: F # غير مكسورة، لأنه ليس لديه دعم مستوى اللغة ل ExtensionMethodAttribute, ويتطلب من طرق تمديد CLS كطرق ثابتة.

هل كانت مفيدة؟

المحلول

تغيير توقيع طريقة

النوع: كسر المستوى الثنائي

اللغات المتأثرة: C # (VB و F # على الأرجح، ولكن لم تختبر)

API قبل التغيير

public static class Foo
{
    public static void bar(int i);
}

API بعد التغيير

public static class Foo
{
    public static bool bar(int i);
}

نموذج عميل العينة يعمل قبل التغيير

Foo.bar(13);

نصائح أخرى

إضافة معلمة ذات قيمة افتراضية.

نوع من استراحة: استراحة المستوى الثنائي

حتى إذا كان رمز مصدر الاتصال لا يحتاج إلى التغيير، فلا يزال يحتاج إلى إعادة ترجمة (مثل عند إضافة معلمة منتظمة).

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

API قبل التغيير

public void Foo(int a) { }

API بعد التغيير

public void Foo(int a, string b = null) { }

نموذج رمز العميل المكسور بعد ذلك

Foo(5);

يحتاج رمز العميل إلى إعادة ترجمة في Foo(5, null) في مستوى bytecode. سوف تتضمن التجميع المسمى فقط Foo(int, string), ، ليس Foo(int). وبعد وذلك لأن قيم المعلمة الافتراضية هي ميزة لغة بحتة، لا يعرف وقت التشغيل .NET أي شيء عنها. (هذا يشرح أيضا لماذا يجب أن تكون القيم الافتراضية ثوابت زمنية في C #).

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

إعادة صياغة أعضاء الفصل في فئة أساسية

نوع: ليس استراحة!

اللغات المتأثرة: لا شيء (أي لا شيء مكسور)

API قبل التغيير:

class Foo
{
    public virtual void Bar() {}
    public virtual void Baz() {}
}

API بعد التغيير:

class FooBase
{
    public virtual void Bar() {}
}

class Foo : FooBase
{
    public virtual void Baz() {}
}

نموذج التعليمات البرمجية الذي يحتفظ بالعمل طوال التغيير (على الرغم من أنني كنت أتوقع من كسره):

// C++/CLI
ref class Derived : Foo
{
   public virtual void Baz() {{

   // Explicit override    
   public virtual void BarOverride() = Foo::Bar {}
};

ملاحظات:

C ++ / CLI هي لغة .NET الوحيدة التي لديها إنشاء مشابه لتنفيذ الواجهة الصريحة لأعضاء فئة الأساس الافتراضية - "التجاوز الصريح". أتوقع تماما أن يؤدي ذلك إلى نفس النوع من الكسر كما هو الحال عند نقل أعضاء الواجهة إلى واجهة أساسية (نظرا لأن IL الذي تم إنشاؤه للتجاوز الصريح هو نفسه التنفيذ الصريح). لمدهشتي، هذا ليس هو الحال - على الرغم من أن IL الذي تم إنشاؤه لا يزال يحدد ذلك BarOverride تجاوز Foo::Bar بدلا من FooBase::Bar, ، لودر التجميع ذكي بما يكفي لاستبدال واحد لآخر بشكل صحيح دون أي شكاوى - على ما يبدو، حقيقة ذلك Foo هو فئة ما يجعل الفرق. إذهب واستنتج...

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

إعادة تكوين الأعضاء واجهة في واجهة قاعدة

النوع: فواصل في كل من المصدر والمستويات الثنائية

اللغات المتأثرة: C #، VB، C ++ / CLI، F # (للاستراحة المصدر؛ ثنائي واحد يؤثر بشكل طبيعي على أي لغة)

API قبل التغيير:

interface IFoo
{
    void Bar();
    void Baz();
}

API بعد التغيير:

interface IFooBase 
{
    void Bar();
}

interface IFoo : IFooBase
{
    void Baz();
}

نموذج رمز العميل الذي يتم كسره عن طريق التغيير على المستوى المصدر:

class Foo : IFoo
{
   void IFoo.Bar() { ... }
   void IFoo.Baz() { ... }
}

نموذج رمز العميل الذي يتم كسره عن طريق التغيير على المستوى الثنائي؛

(new Foo()).Bar();

ملاحظات:

من أجل استراحة مستوى المصدر، المشكلة هي أن C #، VB و C ++ / CLI جميع تتطلب بالضبط اسم الواجهة في إعلان تنفيذ عضو الواجهة؛ وبالتالي، إذا تم نقل العضو إلى واجهة أساسية، فلن يتم تجميع التعليمات البرمجية.

بسبب كسر ثنائي يرجع إلى حقيقة أن طرق الواجهة مؤهلة بالكامل في IL الناتجة عن تطبيقات واضحة، يجب أن يكون اسم الواجهة هناك أيضا بالضبط.

التنفيذ الضمني الذي توفره (أي C # و C ++ / CLI، ولكن ليس VB) يعمل بشكل جيد على المستوى المصدر والثنائي. مكالمات الأسلوب لا تنقطع أيضا.

إعادة ترتيب القيم المرفعة

نوع من استراحة: تغيير مستوى الدلالات الهادئة على مستوى المصدر / المستوى الثنائي

تتأثر اللغات: الكل

سيتم إعادة ترتيب القيم العدادة الحفاظ على التوافق على مستوى المصدر حيث أن الحرفية لها نفس الاسم، ولكن سيتم تحديث مؤشراتها الترتيبية، والتي يمكن أن تسبب بعض أنواع فواصل صامتة على مستوى المصدر.

والأسوأ من ذلك هو فواصل صامتة على المستوى الثنائي الذي يمكن تقديمه إذا لم يتم إعادة ترجمة رمز العميل مقابل إصدار API الجديد. القيم Enum هي ثوابت تجميع الوقت، وعلى هذا النحو يتم تخبز أي استخدامات لهم في IL في جمعية العميل. هذه الحالة يمكن أن تكون من الصعب بشكل خاص في بعض الأحيان.

API قبل التغيير

public enum Foo
{
   Bar,
   Baz
}

API بعد التغيير

public enum Foo
{
   Baz,
   Bar
}

عينة رمز العميل الذي يعمل ولكن مكسور بعد ذلك:

Foo.Bar < Foo.Baz

هذا هو حقا شيء نادر للغاية في الممارسة العملية، ولكن مع ذلك من المفاجئة عندما يحدث ذلك.

إضافة أعضاء غير مثاليين جديدين

النوع: كسر مستوى الفاصل أو دلالات هادئة التغيير.

تتأثر اللغات: C #، VB

اللغات غير المتأثرة: F #، C ++ / CLI

API قبل التغيير:

public class Foo
{
}

API بعد التغيير:

public class Foo
{
    public void Frob() {}
}

نموذج رمز العميل الذي يتم كسره عن طريق التغيير:

class Bar
{
    public void Frob() {}
}

class Program
{
    static void Qux(Action<Foo> a)
    {
    }

    static void Qux(Action<Bar> a)
    {
    }

    static void Main()
    {
        Qux(x => x.Frob());        
    }
}

ملاحظات:

المشكلة هنا ناجمة عن استنتاج نوع Lambda في C # و VB في وجود قرار الزائد. يتم استخدام شكل محدود من الكتابة بالبط هنا لكسر العلاقات حيث يطابق أكثر من نوع واحد، عن طريق التحقق مما إذا كان جسم Lambda منطقي بنوع معين - إذا كان نوع واحد فقط ينتج عنه الجسم القابل للتجميع، فقد يتم اختيار ذلك.

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

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

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

تحويل تطبيق واجهة ضمنية إلى واحد صريح.

نوع من الراحة: المصدر وثنائي

تتأثر اللغات: الكل

هذا هو حقا مجرد تباين لتغيير إمكانية الوصول إلى الطريقة - لها أكثر دقة بقليل قليلا نظرا لأنها سهلة التغاضي عن حقيقة أن الوصول إلى طرق الواجهة بالضرورة من خلال مرجع إلى نوع الواجهة.

API قبل التغيير:

public class Foo : IEnumerable
{
    public IEnumerator GetEnumerator();
}

API بعد التغيير:

public class Foo : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator();
}

نموذج عميل نموذج يعمل قبل التغيير ومكتب بعد ذلك:

new Foo().GetEnumerator(); // fails because GetEnumerator() is no longer public

تحويل واجهة واضحة إلى واحدة ضمنية.

نوع من الراحة: المصدر

تتأثر اللغات: الكل

إن إعادة صياغة تنفيذ واجهة صريحة في واحدة ضمنية هي أكثر دقة في كيفية كسر API. على السطح، يبدو أن هذا يجب أن يكون آمنا نسبيا، ومع ذلك، عند دمج الميراث يمكن أن يسبب مشاكل.

API قبل التغيير:

public class Foo : IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator() { yield return "Foo"; }
}

API بعد التغيير:

public class Foo : IEnumerable
{
    public IEnumerator GetEnumerator() { yield return "Foo"; }
}

نموذج عميل نموذج يعمل قبل التغيير ومكتب بعد ذلك:

class Bar : Foo, IEnumerable
{
    IEnumerator IEnumerable.GetEnumerator() // silently hides base instance
    { yield return "Bar"; }
}

foreach( var x in new Bar() )
    Console.WriteLine(x);    // originally output "Bar", now outputs "Foo"

تغيير حقل إلى خاصية

نوع من الاستراحة: API

تتأثر اللغات: Visual Basic و C # *

معلومات: عند تغيير حقل عادي أو متغير في خاصية في Visual Basic، فإن أي رمز خارجي يشير إلى هذا العضو بأي طريقة ستحتاج إلى إعادة ترجمة.

API قبل التغيير:

Public Class Foo    
    Public Shared Bar As String = ""    
End Class

API بعد التغيير:

Public Class Foo
    Private Shared _Bar As String = ""
    Public Shared Property Bar As String
        Get
            Return _Bar
        End Get
        Set(value As String)
            _Bar = value
        End Set
    End Property
End Class    

عينة رمز العميل الذي يعمل ولكن مكسور بعد ذلك:

Foo.Bar = "foobar"

إضافة مساحة الاسم

Source-Level Break / مستوى مصدر المستوى

نظرا لأن دقة مساحة الاسم تعمل في VB.NET، يمكن أن يؤدي إضافة مساحة اسم إلى مكتبة إلى رمز Visual Basic الذي تم تجميعه مع إصدار سابق من API لا يترجم مع إصدار جديد.

عينة رمز العميل:

Imports System
Imports Api.SomeNamespace

Public Class Foo
    Public Sub Bar()
        Dim dr As Data.DataRow
    End Sub
End Class

إذا قمت بإصدار إصدار جديد من API مساحة الاسم Api.SomeNamespace.Data, ، ثم الرمز أعلاه لن يترجم.

يصبح أكثر تعقيدا من واردات مساحة الاسم على مستوى المشروع. إذا Imports System يتم حذفها من الرمز أعلاه، ولكن System يتم استيراد مساحة الاسم على مستوى المشروع، ثم قد لا يزال الرمز ينتج عنه خطأ.

ومع ذلك، إذا كان API يتضمن فئة DataRow في لها Api.SomeNamespace.Data مساحة الاسم، ثم Code سوف تجميع ولكن dr سيكون مثالا System.Data.DataRow عند تجميعها مع الإصدار القديم من API و Api.SomeNamespace.Data.DataRow عند ترجمة الإصدار الجديد من API.

حجة إعادة تسمية

استراحة مستوى المصدر

تغيير أسماء الحجج هو تغيير كسر في vb.net من الإصدار 7 (؟) (.NET الإصدار 1؟) و C # .NET من الإصدار 4 (.NET الإصدار 4).

API قبل التغيير:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
    }
}

API بعد التغيير:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string y) {
           ...
        }
    }
}

عينة رمز العميل:

Api.SomeNamespace.Foo.Bar(x:"hi"); //C#
Api.SomeNamespace.Foo.Bar(x:="hi") 'VB

المعلمات المرجع

استراحة مستوى المصدر

إضافة طريقة تجاوزت مع نفس التوقيع إلا أن المعلمة واحدة يتم تمريرها حسب المرجع بدلا من القيمة سوف تسبب مصدر VB الذي يشير إلى أن API لتكون غير قادر على حل الوظيفة. ليس لدى Visual Basic أي طريقة (؟) للتمييز بين هذه الأساليب عند نقطة الاتصال ما لم يكن لديهم أسماء وسيطة مختلفة، لذلك قد يتسبب هذا التغيير في أن يكون كل من الأعضاء غير قابل للاستخدام من رمز VB.

API قبل التغيير:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
    }
}

API بعد التغيير:

namespace SomeNamespace {
    public class Foo {
        public static void Bar(string x) {
           ...
        }
        public static void Bar(ref string x) {
           ...
        }
    }
}

عينة رمز العميل:

Api.SomeNamespace.Foo.Bar(str)

مجال تغيير الممتلكات

Binary-Level Break / Break-Level Break

إلى جانب استراحة المستوى الثنائي الواضح، يمكن أن يتسبب ذلك في استراحة على مستوى المصدر إذا تم تمرير العضو إلى طريقة مرجعية.

API قبل التغيير:

namespace SomeNamespace {
    public class Foo {
        public int Bar;
    }
}

API بعد التغيير:

namespace SomeNamespace {
    public class Foo {
        public int Bar { get; set; }
    }
}

عينة رمز العميل:

FooBar(ref Api.SomeNamespace.Foo.Bar);

تغيير API:

  1. إضافة السمة [العاصفة] (التي تغطيها كيندا هذا مع ذكر السمات؛ ومع ذلك، يمكن أن يكون هذا تغييرا وكسر عند استخدام التحذير - خطأ.)

كسر المستوى الثنائي:

  1. نقل نوع من تجميع إلى آخر
  2. تغيير مساحة اسم النوع
  3. إضافة نوع فئة قاعدة من تجميع آخر.
  4. إضافة عضو جديد (محمي الأحداث) التي تستخدم نوع من تجميع آخر (Class2) كقارب حجة القالب.

    protected void Something<T>() where T : Class2 { }
    
  5. تغيير فئة الطفل (Class3) للاستمتاع من نوع في تجميع آخر عندما يتم استخدام الفئة كوسيطة القالب لهذه الفئة.

    protected class Class3 : Class2 { }
    protected void Something<T>() where T : Class3 { }
    

تغيير دلالات هادئة على مستوى المصدر:

  1. إضافة / إزالة / تغيير تجاوزات المساواة ()، gethashcode () أو tostring ()

(غير متأكد من أين هذه الملاءمة)

تغييرات النشر:

  1. إضافة / إزالة التبعيات / المراجع
  2. تحديث التبعيات إلى الإصدارات الأحدث
  3. تغيير "النظام الأساسي" بين x86 أو Itanium أو x64 أو anycpu
  4. المبنى / الاختبار على تثبيت إطار مختلف (أي تثبيت 3.5 على مربع .NET 2.0 يسمح مكالمات API التي تتطلب بعد ذلك .NET 2.0 SP2)

تغييرات bootstrap / التكوين:

  1. إضافة / إزالة / تغيير خيارات التكوين المخصص (إعدادات IE App.config)
  2. مع الاستخدام الشديد ل IOC / DI في تطبيقات اليوم، من الضروري إعادة تكوين و / أو تغيير رمز bootstrapping لرمز DI.

تحديث:

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

إضافة طرق الزائدة إلى تحديد استخدام المعلمات الافتراضية

نوع من استراحة: تغيير مستوى الدلالات الهادئة على مستوى المصدر

نظرا لأن المحول البرمجي يحول الأسلوب يدعو مع قيم المعلمة الافتراضية المفقودة إلى مكالمة صريحة مع القيمة الافتراضية على جانب الاتصال، يتم تقديم التوافق للحصول على التعليمات البرمجية الموجودة؛ سيتم العثور على طريقة مع التوقيع الصحيح لجميع التعليمات البرمجية التي تم تجميعها مسبقا.

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

API قبل التغيير

  public int MyMethod(int mandatoryParameter, int optionalParameter = 0)
  {
     return mandatoryParameter + optionalParameter;
  }    

API بعد التغيير

  public int MyMethod(int mandatoryParameter, int optionalParameter)
  {
     return mandatoryParameter + optionalParameter;
  }

  public int MyMethod(int mandatoryParameter)
  {
     return MyMethod(mandatoryParameter, 0);
  }

نموذج التعليمات البرمجية التي ستظل تعمل

  public int CodeNotDependentToNewVersion()
  {
     return MyMethod(5, 6); 
  }

نموذج التعليمات البرمجية الذي يعتمد الآن على الإصدار الجديد عند تجميع

  public int CodeDependentToNewVersion()
  {
     return MyMethod(5); 
  }

إعادة تسمية واجهة

كيندا من استراحة: المصدر و الثنائية

تتأثر اللغات: على الأرجح كل شيء، تم اختباره في C #.

API قبل التغيير:

public interface IFoo
{
    void Test();
}

public class Bar
{
    IFoo GetFoo() { return new Foo(); }
}

API بعد التغيير:

public interface IFooNew // Of the exact same definition as the (old) IFoo
{
    void Test();
}

public class Bar
{
    IFooNew GetFoo() { return new Foo(); }
}

عينة رمز العميل الذي يعمل ولكن مكسور بعد ذلك:

new Bar().GetFoo().Test(); // Binary only break
IFoo foo = new Bar().GetFoo(); // Source and binary break

طريقة الحمولة الزائدة مع معلمة من النوع nullable

عطوف: استراحة مستوى المصدر

تتأثر اللغات: C #، VB

API قبل التغيير:

public class Foo
{
    public void Bar(string param);
}

API بعد التغيير:

public class Foo
{
    public void Bar(string param);
    public void Bar(int? param);
}

عينة رمز العميل يعمل قبل التغيير وكسر بعد:

new Foo().Bar(null);

استثناء: المكالمة غامضة بين الأساليب أو الخصائص التالية.

الترقية إلى طريقة تمديد

النوع: استراحة مستوى المصدر

اللغات المتأثرة: C # V6 وأعلى (ربما الآخرين؟)

API قبل التغيير:

public static class Foo
{
    public static void Bar(string x);
}

API بعد التغيير:

public static class Foo
{
    public void Bar(this string x);
}

عينة رمز العميل تعمل قبل التغيير وكسر بعد:

using static Foo;

class Program
{
    static void Main() => Bar("hello");
}

مزيد من المعلومات: https://github.com/dotnet/csharplang/issues/665.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top