문제

.NET/CLR의 API 버전 관리, 특히 API 변경이 클라이언트 애플리케이션을 중단시키는 방법과 중단되지 않는 방법에 관해 가능한 한 많은 정보를 수집하고 싶습니다.먼저 몇 가지 용어를 정의해 보겠습니다.

API 변경 - 공개 멤버를 포함하여 공개적으로 표시되는 유형 정의의 변경.여기에는 유형 및 멤버 이름 변경, 유형의 기본 유형 변경, 유형의 구현된 인터페이스 목록에서 인터페이스 추가/제거, 멤버 추가/제거(오버로드 포함), 멤버 가시성 변경, 메소드 및 유형 매개변수 이름 변경, 기본값 추가가 포함됩니다. 메서드 매개변수의 경우 유형 및 멤버에 대한 특성 추가/제거, 유형 및 멤버에 대한 일반 유형 매개변수 추가/제거(놓친 것이 있나요?)여기에는 회원 기관의 변경 사항이나 개인 회원의 변경 사항(예:우리는 반사를 고려하지 않습니다).

바이너리 수준 중단 - 이전 버전의 API에 대해 컴파일된 클라이언트 어셈블리가 잠재적으로 새 버전으로 로드되지 않는 API 변경입니다.예:이전과 같은 방식으로 호출이 허용되더라도 메서드 시그니처를 변경합니다(예:유형/매개변수 기본값 오버로드를 반환하는 경우 void).

소스 수준 중단 - 이전 버전의 API에 대해 컴파일하기 위해 작성된 기존 코드가 잠재적으로 새 버전과 컴파일되지 않는 API 변경입니다.그러나 이미 컴파일된 클라이언트 어셈블리는 이전과 같이 작동합니다.예:이전에는 명확했던 메서드 호출에서 모호성을 초래할 수 있는 새로운 오버로드를 추가합니다.

소스 수준의 조용한 의미 변경 - 이전 버전의 API에 대해 컴파일하기 위해 작성된 기존 코드로 인해 의미가 조용히 변경되는 API 변경입니다.다른 메소드를 호출하여.그러나 코드는 경고/오류 없이 계속 컴파일되어야 하며 이전에 컴파일된 어셈블리는 이전처럼 작동해야 합니다.예:기존 클래스에 새 인터페이스를 구현하면 오버로드 해결 중에 다른 오버로드가 선택됩니다.

궁극적인 목표는 가능한 한 많은 중단 및 조용한 의미 체계 API 변경 사항을 목록화하고 중단의 정확한 효과와 이에 영향을 받는 언어와 영향을 받지 않는 언어를 설명하는 것입니다.후자를 확장하려면 다음을 수행하십시오.일부 변경 사항은 모든 언어에 보편적으로 영향을 미칩니다(예:인터페이스에 새 멤버를 추가하면 모든 언어에서 해당 인터페이스의 구현이 중단됩니다. 일부는 중단을 위해 작동하기 위해 매우 구체적인 언어 의미 체계가 필요합니다.여기에는 가장 일반적으로 메소드 오버로딩이 포함되며 일반적으로 암시적 유형 변환과 관련된 모든 것이 포함됩니다.CLS 규격 언어(예:최소한 CLI 사양에 정의된 "CLS 소비자"의 규칙을 준수하는 것) - 누군가가 여기에서 내가 틀렸다고 정정해 준다면 감사하겠지만 - 따라서 이것은 언어별로 진행되어야 합니다.가장 관심을 끄는 것은 당연히 .NET과 함께 기본적으로 제공되는 것입니다.C#, VB 및 F#;그러나 IronPython, IronRuby, Delphi Prism 등과 같은 다른 것들도 관련이 있습니다.코너 케이스가 많을수록 더 흥미로울 것입니다. 멤버 제거와 같은 작업은 매우 자명하지만 예를 들어 멤버 간의 미묘한 상호 작용은 매우 자명합니다.메소드 오버로딩, 선택적/기본 매개변수, 람다 유형 추론 및 변환 연산자는 때때로 매우 놀랄 수 있습니다.

이를 시작하기 위한 몇 가지 예:

새로운 메서드 오버로드 추가

친절한:소스 레벨 중단

영향을 받는 언어: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

영향을 받지 않는 언어:에프#

변경 전 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

영향을 받지 않는 언어:에프#

변경 전 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#은 매개 변수의 기본값을 호출 어셈블리로 직접 컴파일하기 때문입니다. 그것은 당신이 다시 컴파일하지 않으면, 이전 어셈블리가 인수가 적은 메소드를 호출하려고하기 때문에 누락 된 methodexception을 얻을 수 있음을 의미합니다.

변경 전 API

public void Foo(int a) { }

변경 후 API

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

나중에 끊어진 클라이언트 코드 샘플

Foo(5);

클라이언트 코드를 다시 컴파일해야합니다 Foo(5, null) 바이트 코드 수준에서. 호출 된 어셈블리에는 만 포함됩니다 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은 명시 적 구현과 동일하기 때문에). 놀랍게도, 이것은 사실이 아닙니다. BarOverride 재정의 Foo::Bar 보다는 FooBase::Bar, 어셈블리 로더는 불만없이 하나를 올바르게 대체 할만 큼 똑똑합니다. Foo 수업은 차이를 만드는 것입니다. 그림을 이동...

이것은 아마도 "인터페이스 멤버 추가/제거"에 대한 그다지 명확하지 않은 특수 사례이며, 다음에 게시할 다른 사례에 비추어 볼 때 자체적으로 항목을 추가할 가치가 있다고 생각했습니다.그래서:

인터페이스 멤버를 기본 인터페이스로 리팩터링

친절한:소스 및 바이너리 수준 모두에서 중단

영향을 받는 언어:C#, VB, C++/CLI, F#(소스 중단용;이진수 1은 자연스럽게 모든 언어에 영향을 미칩니다)

변경 전 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 버전에 대해 다시 컴파일되지 않으면 소개 될 수있는 무음 바이너리 레벨 브레이크가 더 나쁜 것도 더 나쁘다. 열거 값은 컴파일 타임 상수이며, 따라서 모든 용도는 클라이언트 어셈블리의 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());        
    }
}

메모:

여기서 문제는 과부하 분해능이있는 C# 및 VB의 Lambda 유형 추론으로 인해 발생합니다. Lambda의 본문이 주어진 유형에 대해 의미가 있는지 확인하여 하나 이상의 유형이 주어진 유형에 대해 의미가 있는지 확인하여 하나의 유형만이 컴파일 가능한 몸체를 초래하는 경우 선택을 선택하여 하나의 유형이 주어진 유형에 대해 의미가 있는지 확인하여 한 유형의 유형이 일치하는 유대를 깨기 위해 여기에 제한된 형태의 오리 타이핑이 사용됩니다.

여기서 위험은 클라이언트 코드가 과부하 된 메소드 그룹을 가질 수 있다는 것입니다. 일부 방법은 자신의 유형에 대한 인수를 취하고 다른 방법은 라이브러리에서 노출 된 유형에 대한 주장을 취할 수 있다는 것입니다. 그의 코드 중 하나라면 멤버의 존재 또는 부재만으로 올바른 방법을 결정하기 위해 유형 추론 알고리즘에 의존하면 클라이언트 유형 중 하나와 동일한 유형 중 하나에 새 멤버를 추가하면 잠재적으로 추론을 던질 수 있습니다. OFF, 오버로드 해상도 동안 모호성을 초래합니다.

유형에 유의하십시오 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"

네임 스페이스 추가

소스 레벨 브레이크 / 소스 수준의 조용한 의미는 변경됩니다

네임 스페이스 해상도가 vb.net에서 작동하는 방식으로 인해 라이브러리에 네임 스페이스를 추가하면 이전 버전의 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 네임 스페이스, 그러면 코드가 컴파일되지만 dr 인스턴스가 될 것입니다 System.Data.DataRow 이전 버전의 API와 컴파일 된 경우 Api.SomeNamespace.Data.DataRow 새 버전의 API로 컴파일 된 경우

인수 이름 변경

소스 수준의 중단

인수 이름을 변경하는 것은 버전 7 (?) (.NET 버전 1?) 및 버전 4 (.NET 버전 4)의 C#.NET에서 vb.net의 깨진 변경입니다.

변경 전 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

ref 매개 변수

소스 수준의 중단

하나의 매개 변수가 값 대신 참조별로 전달되는 것을 제외하고는 동일한 서명으로 메소드를 재정의하면 API를 참조하는 VB 소스가 함수를 해결할 수 없게됩니다. 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)

재산 변경 필드

이진 수준의 브레이크/소스 수준 브레이크

명백한 이진 레벨 브레이크 외에도, 멤버가 참조로 메소드로 전달되면 소스 레벨 브레이크가 발생할 수 있습니다.

변경 전 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. 클래스 가이 클래스의 템플릿 인수로 사용될 때 다른 어셈블리의 유형에서 유형에서 파생되도록 Child Class (Class3) 변경.

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

소스 수준의 조용한 시맨틱 변경 :

  1. equals (), gethashcode () 또는 toString ()의 재정의/제거/변경

(이게 어디에 적합한 지 확실하지 않음)

배포 변경 :

  1. 종속성/참조 추가/제거
  2. 최신 버전으로 의존성을 업데이트합니다
  3. X86, Itanium, X64 또는 AnyCPU 사이의 '대상 플랫폼'변경
  4. 다른 프레임 워크 설치에 대한 빌드/테스트 (예 : .NET 2.0 상자에 3.5 설치 API 호출이 가능합니다.

부트 스트랩/구성 변경 :

  1. 사용자 정의 구성 옵션 추가/제거/변경 (예 : App.Config 설정)
  2. 오늘날의 응용 프로그램에서 IOC/DI를 많이 사용하면 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

무효 유형의 매개 변수를 가진 과부하 메소드

친절한: 소스 수준의 중단

영향을받는 언어 : 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