속성 대신 마커 인터페이스를 사용해야 하는 강력한 이유
이전에 스택 오버플로에서 마커 인터페이스(구성원이 없는 인터페이스)보다 속성을 선호해야 한다는 것에 대해 논의했습니다.MSDN에 대한 인터페이스 설계 문서에서도 이 권장 사항을 주장합니다.
마커 인터페이스(구성원이 없는 인터페이스)를 사용하지 않도록 합니다.
사용자 지정 특성은 유형을 표시하는 방법을 제공합니다.사용자 지정 특성에 대한 자세한 내용은 사용자 지정 특성 쓰기를 참조하십시오.코드가 실행될 때까지 속성 확인을 연기할 수 있는 경우 사용자 지정 특성이 선호됩니다.시나리오에서 컴파일 시간 확인이 필요한 경우 이 지침을 준수할 수 없습니다.
이 권장 사항을 적용하기 위한 FxCop 규칙도 있습니다.
빈 인터페이스 사용 안 함
인터페이스는 동작 또는 사용 계약을 제공하는 구성원을 정의합니다.인터페이스에서 설명하는 기능은 상속 계층에서 유형이 나타나는 위치에 관계없이 모든 유형에서 사용할 수 있습니다.A 유형은 인터페이스 멤버에 대한 구현을 제공하여 인터페이스를 구현합니다.빈 인터페이스는 구성원을 정의하지 않으므로 구현할 수 있는 계약을 정의하지 않습니다.
설계에 유형이 구현될 빈 인터페이스가 포함된 경우 인터페이스를 마커로 사용하거나 유형 그룹을 식별하는 방법으로 사용할 수 있습니다.런타임에 이 식별이 발생할 경우 올바른 방법은 사용자 지정 특성을 사용하는 것입니다.특성의 유무 또는 특성의 속성을 사용하여 대상 유형을 식별합니다.컴파일 시 식별이 이루어져야 하는 경우 빈 인터페이스를 사용할 수 있습니다.
이 문서에서는 경고를 무시할 수 있는 한 가지 이유, 즉 유형에 대한 컴파일 시간 식별이 필요한 경우를 설명합니다.(이는 인터페이스 설계 문서와 일치합니다.)
인터페이스가 컴파일 시 유형 집합을 식별하는 데 사용되는 경우 이 규칙에서 경고를 제외하는 것이 안전합니다.
실제 질문은 다음과 같습니다.Microsoft는 Framework 클래스 라이브러리 설계 시 자체 권장 사항을 준수하지 않았습니다(적어도 몇 가지 경우).IR에는 SessionState 인터페이스와 IReadOnlySessionState 인터페이스가 필요합니다.이러한 인터페이스는 ASP에서 사용됩니다.특정 처리기에 대해 세션 상태를 활성화해야 하는지 여부를 확인하는 NET 프레임워크입니다.분명히, 이것은 형식의 컴파일 시간 식별에 사용되지 않습니다.왜 안 그랬을까요?저는 두 가지 잠재적인 이유를 생각할 수 있습니다.
미세 최적화:개체가 인터페이스를 구현하는지 확인하는 중(
obj is IReadOnlySessionState
)는 반사를 사용하여 속성을 확인하는 것보다 빠릅니다(type.IsDefined(typeof(SessionStateAttribute), true)
대부분의 경우 이 차이는 무시할 수 있지만 실제로는 ASP의 성능에 중요한 코드 경로에 영향을 미칠 수 있습니다.NET 런타임.그러나 각 처리기 유형에 대한 결과를 캐싱하는 것과 같이 사용할 수 있는 해결 방법이 있습니다.흥미로운 점은 ASMX 웹 서비스(유사한 성능 특성의 영향을 받는)가 실제로 이러한 목적으로 속성의 속성을 사용한다는 것입니다.인터페이스 구현은 서드 파티의 속성으로 유형을 장식하는 것보다 지원될 가능성이 더 높습니다.NET 언어.ASP 이후.NET은 언어에 구애받지 않고 ASP로 설계되었습니다.NET은 다음을 기반으로 위의 인터페이스를 구현하는 유형(CodeDom의 도움을 받아 타사 언어로 가능)에 대한 코드를 생성합니다.
EnableSessionState
명령어의 속성은 속성 대신 인터페이스를 사용하는 것이 더 의미가 있을 수 있습니다.
속성 대신 마커 인터페이스를 사용하는 설득력 있는 이유는 무엇입니까?
이것은 단순히 (초기) 최적화입니까, 아니면 프레임워크 설계의 작은 실수입니까?(그들은 반사가 "붉은 눈을 가진 큰 괴물"이라고 생각합니까?생각은?
일반적으로 "마커 인터페이스"는 파생된 유형의 표시를 해제할 수 없기 때문에 사용하지 않습니다.그러나 여기에 내장된 메타데이터 지원보다 마커 인터페이스를 선호하는 구체적인 사례가 몇 가지 있습니다.
- 런타임 성능에 민감한 상황입니다.
- 주석 또는 속성을 지원하지 않는 언어와의 호환성.
- 관심 코드가 메타데이터에 액세스할 수 없는 컨텍스트입니다.
- 일반 제약 조건 및 일반 분산(일반적으로 집합)을 지원합니다.
제네릭 유형의 경우 마커 인터페이스에 동일한 제네릭 매개 변수를 사용할 수 있습니다.속성으로는 이 작업을 수행할 수 없습니다.
interface MyInterface<T> {}
class MyClass<T, U> : MyInterface<U> {}
class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}
이러한 종류의 인터페이스는 유형을 다른 유형과 연결하는 데 유용할 수 있습니다.
마커 인터페이스의 또 다른 유용한 용도는 다음과 같은 종류의 혼합을 만들 때입니다.
interface MyMixin {}
static class MyMixinMethods {
public static void Method(this MyMixin self) {}
}
class MyClass : MyMixin {
}
비순환 방문자 패턴도 그것들을 사용합니다."퇴화 인터페이스"라는 용어도 때때로 사용됩니다.
업데이트:
이것이 중요한지는 모르겠지만, 저는 포스트 컴파일러가 작업할 수업을 표시하기 위해 그것들을 사용했습니다.
마이크로소프트는 이 지침을 만들 때 엄격하게 따르지 않았습니다.NET 1.0은 가이드라인이 프레임워크와 함께 발전했기 때문에 API를 변경하기에 너무 늦을 때까지 배우지 못한 규칙이 있습니다.
IIRC, 당신이 언급한 예들은 BCL 1.0에 속하므로 그것이 설명이 될 것입니다.
이는 프레임워크 설계 지침에 설명되어 있습니다.
그렇긴 하지만, 이 책은 또한 "[속성 테스트는 유형 검사보다 훨씬 더 비용이 많이 든다"고 언급합니다(리코 마리안리의 사이드바에서).
컴파일 시간 확인을 위해 마커 인터페이스가 필요한 경우도 있지만 속성으로는 불가능합니다.그러나 저는 책(88쪽)에 제시된 예를 납득할 수 없다고 생각하기 때문에 여기서 반복하지 않겠습니다.
성능 측면에서:
반사 때문에 마커 특성이 마커 인터페이스보다 느립니다.를 캐시하지 반를캐지않으호다출니합면하시사다니▁if로 전화하세요.GetCustomAttributes
항상 성능 병목 현상이 발생할 수 있습니다.이전에 이를 벤치마킹하여 마커 인터페이스를 사용하면 캐시 리플렉션을 사용할 때도 성능 면에서 승리합니다.
이것은 자주 호출되는 코드에서 사용하는 경우에만 적용됩니다.
BenchmarkDotNet=v0.10.14, OS=Windows 10.0.16299.371 (1709/FallCreatorsUpdate/Redstone3)
Intel Core i5-2400 CPU 3.10GHz (Sandy Bridge), 1 CPU, 4 logical and 4 physical cores
Frequency=3020482 Hz, Resolution=331.0730 ns, Timer=TSC
.NET Core SDK=2.1.300-rc1-008673
[Host] : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT
Core : .NET Core 2.0.7 (CoreCLR 4.6.26328.01, CoreFX 4.6.26403.03), 64bit RyuJIT
Job=Core Runtime=Core
Method | Mean | Error | StdDev | Rank |
--------------------------- |--------------:|-----------:|-----------:|-----:|
CastIs | 0.0000 ns | 0.0000 ns | 0.0000 ns | 1 |
CastAs | 0.0039 ns | 0.0059 ns | 0.0052 ns | 2 |
CustomAttribute | 2,466.7302 ns | 18.5357 ns | 17.3383 ns | 4 |
CustomAttributeWithCaching | 25.2832 ns | 0.5055 ns | 0.4729 ns | 3 |
하지만 큰 차이는 없습니다.
namespace BenchmarkStuff
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class CustomAttribute : Attribute
{
}
public interface ITest
{
}
[Custom]
public class Test : ITest
{
}
[CoreJob]
[RPlotExporter, RankColumn]
public class CastVsCustomAttributes
{
private Test testObj;
private Dictionary<Type, bool> hasCustomAttr;
[GlobalSetup]
public void Setup()
{
testObj = new Test();
hasCustomAttr = new Dictionary<Type, bool>();
}
[Benchmark]
public void CastIs()
{
if (testObj is ITest)
{
}
}
[Benchmark]
public void CastAs()
{
var itest = testObj as ITest;
if (itest != null)
{
}
}
[Benchmark]
public void CustomAttribute()
{
var customAttribute = (CustomAttribute)testObj.GetType().GetCustomAttributes(typeof(CustomAttribute), false).SingleOrDefault();
if (customAttribute != null)
{
}
}
[Benchmark]
public void CustomAttributeWithCaching()
{
var type = testObj.GetType();
bool hasAttr = false;
if (!hasCustomAttr.TryGetValue(type, out hasAttr))
{
hasCustomAttr[type] = type.CustomAttributes.SingleOrDefault(attr => attr.AttributeType == typeof(CustomAttribute)) != null;
}
if (hasAttr)
{
}
}
}
public static class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<CastVsCustomAttributes>();
}
}
}
저는 강력하게 마커 인터페이스를 지지합니다.난 속성을 좋아한 적이 없습니다.저는 그것들을 예를 들어 디버거들이 볼 수 있도록 의도된 클래스와 구성원들을 위한 일종의 메타 정보로 봅니다.예외와 마찬가지로 일반적인 처리 논리에 영향을 주어서는 안 된다고 생각합니다.
코딩의 관점에서, 나는 내장된 키워드 때문에 Marker Interface 구문을 선호한다고 생각합니다.as
그리고.is
속성 표시에는 코드가 조금 더 필요합니다.
[MarkedByAttribute]
public class MarkedClass : IMarkByInterface
{
}
public class MarkedByAttributeAttribute : Attribute
{
}
public interface IMarkByInterface
{
}
public static class AttributeExtension
{
public static bool HasAttibute<T>(this object obj)
{
var hasAttribute = Attribute.GetCustomAttribute(obj.GetType(), typeof(T));
return hasAttribute != null;
}
}
코드를 사용하기 위한 몇 가지 테스트:
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ClassMarkingTests
{
private MarkedClass _markedClass;
[TestInitialize]
public void Init()
{
_markedClass = new MarkedClass();
}
[TestMethod]
public void TestClassAttributeMarking()
{
var hasMarkerAttribute = _markedClass.HasAttibute<MarkedByAttributeAttribute>();
Assert.IsTrue(hasMarkerAttribute);
}
[TestMethod]
public void TestClassInterfaceMarking()
{
var hasMarkerInterface = _markedClass as IMarkByInterface;
Assert.IsTrue(hasMarkerInterface != null);
}
}
언급URL : https://stackoverflow.com/questions/2086451/compelling-reasons-to-use-marker-interfaces-instead-of-attributes
'programing' 카테고리의 다른 글
Mariadb가 절전 모드 연결을 자동으로 닫는 시간 (0) | 2023.08.14 |
---|---|
closedXml c#에서 자동 필터를 비활성화하는 방법은 무엇입니까? (0) | 2023.08.14 |
JavaScript에서 본문 또는 문서에 div 요소 추가 (0) | 2023.08.14 |
PL/SQL에서 RECORD 컬렉션을 수동으로 초기화하는 방법은 무엇입니까? (0) | 2023.08.14 |
실행할 참조 어셈블리를 로드할 수 없습니다. (0) | 2023.08.14 |