.NET의 API 혁신적인 변경 사항에 대한 최종 가이드
.NET/CLR의 API 버전 관리와 관련하여 가능한 한 많은 정보를 수집하고 싶습니다. 특히 API 변경이 클라이언트 애플리케이션을 손상시키거나 손상시키지 않습니다.먼저 몇 가지 용어를 정의해 보겠습니다.
API 변경 - 공개 멤버를 포함하여 공개적으로 볼 수 있는 형식 정의의 변경입니다.여기에는 유형 및 멤버 이름 변경, 유형의 기본 유형 변경, 유형의 구현된 인터페이스 목록에서 인터페이스 추가/제거, 멤버 추가/제거(오버로드 포함), 멤버 가시성 변경, 메서드 및 유형 매개 변수의 이름 변경, 메서드 매개 변수의 기본값 추가,유형 및 멤버에 대한 속성 추가/제거, 유형 및 멤버에 대한 일반 유형 매개 변수 추가/제거(누락된 항목이 있습니까?)여기에는 구성원 단체의 변경 사항이나 개인 구성원에 대한 변경 사항은 포함되지 않습니다(즉, 우리는 반영을 고려하지 않습니다).
이진 수준 중단 - 이전 버전의 API에 대해 컴파일된 클라이언트 어셈블리를 새 버전으로 로드하지 못할 수 있는 API 변경 사항입니다.예: 이전과 동일한 방식으로 호출할 수 있는 경우에도 메서드 시그니처 변경(즉, void to return type/parameter default 값 오버로드).
소스 수준 중단 - 새 버전으로 컴파일되지 않을 수 있는 이전 버전의 API에 대해 컴파일하기 위해 기존 코드가 작성되는 API 변경 사항입니다.그러나 이미 컴파일된 클라이언트 어셈블리는 이전과 같이 작동합니다.예: 이전에는 모호하지 않았던 메서드 호출에 모호성을 초래할 수 있는 새로운 오버로드를 추가합니다.
소스 레벨 조용한 의미 변경 - 이전 버전의 API에 대해 컴파일하기 위해 작성된 기존 코드를 다른 메서드를 호출하는 등 조용히 의미를 변경하는 API 변경.그러나 코드는 경고/오류 없이 컴파일을 계속해야 하며 이전에 컴파일된 어셈블리는 이전과 같이 작동해야 합니다.예: 오버로드 해결 중에 다른 오버로드가 선택되는 결과를 초래하는 기존 클래스에 새 인터페이스를 구현합니다.
궁극적인 목표는 가능한 한 많은 중단 및 조용한 의미 API 변경 사항을 카탈로그화하고, 중단의 정확한 효과와 중단의 영향을 받는 언어와 영향을 받지 않는 언어를 설명하는 것입니다.후자를 확장하려면: 일부 변경 사항은 모든 언어에 보편적으로 영향을 미치지만(예: 인터페이스에 새 멤버를 추가하면 모든 언어에서 해당 인터페이스의 구현이 중단됨), 일부 변경 사항은 휴식을 취하기 위해 실행에 들어가기 위해 매우 구체적인 언어 의미가 필요합니다.여기에는 가장 일반적으로 메서드 오버로드가 포함되며, 일반적으로 암시적 형식 변환과 관련된 모든 작업이 포함됩니다.여기서는 CLS 호환 언어(즉, CLI 사양에 정의된 "CLS 소비자" 규칙을 준수하는 언어)에 대해서도 "최소한의 공통 분모"를 정의할 방법이 없는 것 같습니다. 하지만 누군가가 여기서 제가 틀렸다고 정정해 주시면 감사하겠습니다. 그래서 이것은 언어별로 진행되어야 합니다.C#, VB 및 F#과 같은 .NET과 함께 즉시 제공되는 것이 가장 중요하지만, 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
영향을 받지 않는 언어: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#이 매개 변수의 기본값을 호출 어셈블리에 직접 컴파일하기 때문입니다.즉, 이전 어셈블리가 인수가 적은 메서드를 호출하려고 하므로 재컴파일하지 않으면 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은 명시적 구현과 동일하므로) 인터페이스 구성원을 기본 인터페이스로 이동할 때와 같은 종류의 파손이 발생할 것으로 충분히 예상했습니다.놀랍게도, 생성된 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의 람다 유형 추론에 의해 발생합니다.여기서는 제한된 형태의 오리 유형을 사용하여 둘 이상의 유형이 일치하는 연결을 끊습니다. 람다의 본체가 특정 유형에 적합한지 확인합니다. 하나의 유형만 컴파일 가능한 본체가 되는 경우 해당 유형이 선택됩니다.
여기서 위험한 것은 클라이언트 코드에 오버로드된 메서드 그룹이 있을 수 있다는 것입니다. 메서드 그룹은 일부 메서드는 고유한 형식의 인수를 사용하고 다른 메서드는 라이브러리에 의해 노출된 형식의 인수를 사용합니다.그의 코드 중 하나가 유형 추론 알고리즘에 의존하여 구성원의 존재 여부만을 기준으로 올바른 방법을 결정하는 경우, 클라이언트 유형 중 하나와 동일한 이름을 가진 사용자 유형 중 하나에 새 구성원을 추가하면 잠재적으로 추론이 중단되어 오버로드 해결 중 모호성이 발생할 수 있습니다.
: 유형 유형을 합니다.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에서 네임스페이스 확인이 작동하는 방식 때문입니다.라이브러리에 네임스페이스를 추가하면 이전 버전의 API로 컴파일된 Visual Basic 코드가 새 버전으로 컴파일되지 않을 수 있습니다.
샘플 클라이언트 코드:
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.SomeNamespace.Data.DataRow
새 버전의 API로 컴파일할 때.
인수 이름 바꾸기
소스 레벨 중단
vb.net 에서 인수 이름을 변경하면 버전 7(.Net 버전 1?)과 버전 4(.Net)에서 c#.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
기준 매개변수
소스 레벨 중단
하나의 매개 변수가 값이 아닌 참조로 전달되는 것을 제외하고 동일한 서명으로 메서드 재정의를 추가하면 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 변경:
- [Osoled] 특성 추가(여기에서는 특성을 언급했지만 오류 경고를 사용할 때 변경 사항이 발생할 수 있습니다.)
이진 수준 중단:
- 한 어셈블리에서 다른 어셈블리로 유형 이동
- 유형의 네임스페이스 변경
- 다른 어셈블리에서 기본 클래스 유형을 추가하는 중입니다.
다른 어셈블리(Class2)의 형식을 템플릿 인수 제약 조건으로 사용하는 새 멤버(이벤트 보호됨)를 추가합니다.
protected void Something<T>() where T : Class2 { }
클래스가 이 클래스의 템플릿 인수로 사용될 때 다른 어셈블리의 형식에서 파생되도록 자식 클래스(클래스3) 변경.
protected class Class3 : Class2 { } protected void Something<T>() where T : Class3 { }
소스 수준의 조용한 의미 변경:
- Equals(), GetHashCode() 또는 ToString()의 재정의 추가/제거/변경
(이것들이 어디에 맞는지 확실하지 않음)
배포 변경 사항:
- 종속성/참조 추가/제거
- 최신 버전으로 종속성을 업데이트하는 중
- x86, Itanium, x64 또는 임의의 CPU 간에 '대상 플랫폼' 변경
- 다른 프레임워크 설치(예: 에 3.5 설치)에 대한 구축/테스트Net 2.0 상자를 사용하면 API 호출이 가능합니다.네트 2.0 SP2)
부트스트랩/구성 변경:
- 사용자 지정 구성 옵션 추가/제거/변경(예: App.config 설정)
- 오늘날의 애플리케이션에서 IoC/DI가 많이 사용됨에 따라 DI 종속 코드에 대한 부트스트래핑 코드를 재구성 및/또는 변경해야 합니다.
업데이트:
죄송합니다. 이 문제가 발생한 유일한 이유가 템플릿 제약 조건에서 사용했기 때문인지 몰랐습니다.
종료 기본 매개 변수 사용에 오버로드 메서드를 추가하는 중
휴식의 종류:소스 수준의 조용한 의미 변경
컴파일러는 누락된 기본 매개 변수 값을 가진 메서드 호출을 호출 측의 기본값을 가진 명시적 호출로 변환하기 때문에 기존 컴파일된 코드에 대한 호환성이 제공됩니다. 이전에 컴파일된 모든 코드에 대해 올바른 서명을 가진 메서드가 검색됩니다.
한편, 선택적 매개 변수를 사용하지 않는 호출은 선택적 매개 변수가 없는 새 메서드에 대한 호출로 컴파일됩니다.모든 것이 여전히 정상적으로 작동하지만 호출된 코드가 다른 어셈블리에 있는 경우, 호출된 코드를 새로 컴파일한 코드는 이제 이 어셈블리의 새 버전에 종속됩니다.리팩터 코드가 있는 어셈블리를 배포하지 않고 리팩터 코드를 호출하는 어셈블리를 배포하면 "method not found" 예외가 발생합니다.
변경 전 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# 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
매개 변수가 null인 메서드를 오버로드합니다.
종류: 소스 레벨 중단
영향을 받는 언어: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);
예외:호출이 다음 메서드 또는 속성 사이에서 모호합니다.
Visual Studio Extension NDepend는 API Breaking Changes 범주에 이진 수준 중단을 탐지하는 몇 가지 규칙을 제공합니다.이러한 규칙은 NDepend 기준선이 정의된 경우에만 실행됩니다.
- API 중단 변경 사항: 유형: 이 규칙은 기준선에 공개적으로 표시되는 유형이 더 이상 공개적으로 표시되지 않거나 제거되었는지 경고합니다.이러한 유형을 사용하는 클라이언트 코드가 손상됩니다.
- API 중단 변경 사항: 방법:이 규칙은 기준선에 공개적으로 표시되는 메서드가 더 이상 공개적으로 표시되지 않거나 제거되었는지 경고합니다.이러한 방법을 사용하는 클라이언트 코드는 손상됩니다.메서드 서명이 변경되면 이전 메서드 버전이 제거된 것으로 간주되고 새 메서드 버전이 추가된 것으로 간주되므로 이전 메서드 버전에서 중단된 변경이 감지됩니다.
- API 중단 변경 사항: 필드:이 규칙은 기준선에 공개적으로 표시되는 필드가 더 이상 공개적으로 표시되지 않거나 필드가 제거되었는지 경고합니다.해당 필드를 사용하는 클라이언트 코드가 손상됩니다.
- API 중단 변경 사항: 인터페이스 및 추상 클래스:이 규칙은 공개적으로 표시되는 인터페이스 또는 추상 클래스가 변경되고 새로운 추상 메서드가 포함되어 있는지 또는 일부 추상 메서드가 제거되었는지 여부를 경고합니다.이러한 인터페이스를 구현하거나 추상 클래스에서 파생된 클라이언트 코드가 손상됩니다.
- 손상된 직렬화 가능 유형:이 규칙은 SerializableAttribute 태그가 지정된 유형의 변경 사항을 중단하는 것에 대해 경고합니다.이를 위해 이 규칙은 기준선 이후에 직렬화 가능한 인스턴스 필드가 추가되거나 제거된 직렬화 가능한 유형을 검색합니다.NonSerializedAttribute로 태그가 지정된 필드는 고려하지 않습니다.
- 열거 변경 방지 플래그 상태:이 규칙은 기준선에서 FlagsAttribute로 태그가 지정되었던 열거 유형과 일치하며 더 이상 일치하지 않습니다.또한 FlagsAttribute로 태그가 지정되고 기준선에 태그가 지정되지 않은 반대의 열거 유형과도 일치합니다.FlagsAttribute로 태그가 지정되는 것은 열거에 대한 강력한 속성입니다.행동 측면에서는 많지 않습니다(단, 열거형만 해당).ToString() 메서드 동작은 FlagsAttribute)로 열거형에 태그를 지정할 때 변경되지만 의미 측면에서는 열거형이 값 범위입니까, 플래그 범위입니까?
또한 사용자가 새로운 공개 API 요소를 탐색할 수 있도록 3가지 코드 쿼리가 제안됩니다.
정적 읽기 전용을 상수로 변환
종류: 이진 레벨 브레이크
영향을 받는 언어:C#, VB 및 F#
변경 전 API:
public static class Foo
{
public static readonly string Bar = "Value";
}
변경 후 API:
public static class Foo
{
public const string Bar = "Value";
}
는 새로운 사항을 , "" " " " " " " 입니다. 그렇지 않으면MissingFieldException
던져집니다.
언급URL : https://stackoverflow.com/questions/1456785/a-definitive-guide-to-api-breaking-changes-in-net
'sourcetip' 카테고리의 다른 글
ELIFECYCLE Node.js 오류는 무엇을 의미합니까? (0) | 2023.05.28 |
---|---|
Angular에서 이벤트 이미터에 2개의 매개 변수를 전달하는 방법은 무엇입니까? (0) | 2023.05.28 |
Linux에서 가상 환경을 활성화하려면 어떻게 해야 합니까? (0) | 2023.05.23 |
zure에서 파일 업로드 후 BLOB-URL을 가져오는 방법 (0) | 2023.05.23 |
CSV 파일 읽기 및 배열에 값 저장 (0) | 2023.05.23 |