Meh*_*ari 55 .net asp.net attributes interface marker-interfaces
它已经讨论过堆栈溢出,我们应该更喜欢属性标记接口(接口没有任何成员).MSDN上的接口设计文章也断言了这个建议:
避免使用标记接口(没有成员的接口).
自定义属性提供了标记类型的方法.有关自定义属性的更多信息,请参阅编写自定义属性.如果可以在执行代码之前推迟检查属性,则首选自定义属性.如果您的方案需要编译时检查,则无法遵守此准则.
甚至还有一个FxCop规则来强制执行此建议:
避免空接口
接口定义提供行为或使用合同的成员.无论类型在继承层次结构中出现何种位置,接口描述的功能都可以采用任何类型.类型通过为接口的成员提供实现来实现接口.空接口不定义任何成员,因此,不定义可以实现的合同.
如果您的设计包含期望实现类型的空接口,则可能使用接口作为标记,或者标识一组类型的方法.如果此标识将在运行时发生,则完成此操作的正确方法是使用自定义属性.使用属性的存在或不存在或属性的属性来标识目标类型.如果标识必须在编译时进行,则可以使用空接口.
本文仅说明了您可能忽略警告的一个原因:何时需要对类型进行编译时识别.(这与界面设计文章一致).
如果在编译时使用接口标识一组类型,则可以安全地从此规则中排除警告.
实际问题是:Microsoft在框架类库的设计中(至少在几种情况下)不符合他们自己的建议:IRequiresSessionState接口和IReadOnlySessionState接口.ASP.NET框架使用这些接口来检查它是否应该为特定处理程序启用会话状态.显然,它不用于类型的编译时识别.他们为什么不这样做?我可以想到两个潜在的原因:
微优化:检查对象是否实现接口(obj is IReadOnlySessionState
)比使用反射检查属性(type.IsDefined(typeof(SessionStateAttribute), true)
)更快.大多数时候差异可以忽略不计,但它实际上可能对ASP.NET运行时中的性能关键代码路径很重要.但是,他们可以使用的解决方法就像为每个处理程序类型缓存结果一样.有趣的是,ASMX Web服务(具有类似性能特征)实际上使用EnableSession
属性的WebMethod
属性来实现此目的.
与使用第三方.NET语言的属性装饰类型相比,可能更有可能支持实现接口.由于ASP.NET被设计为与语言无关,并且ASP.NET 根据指令的属性为类型(可能在CodeDom的帮助下以第三方语言生成)生成代码,因此可能会生成更多感觉使用接口而不是属性.EnableSessionState
<%@ Page %>
使用标记接口而不是属性的有说服力的原因是什么?
这只是一个(过早?)优化还是框架设计中的一个小错误?(他们认为反射是"红眼睛的大怪物"吗?)思想?
LBu*_*kin 14
我通常避免使用"标记接口",因为它们不允许您取消标记派生类型.但除此之外,以下是我所看到的一些特定情况,其中标记接口比内置元数据支持更可取:
Jor*_*dão 10
对于泛型类型,您可能希望在标记接口中使用相同的泛型参数.这是属性无法实现的:
interface MyInterface<T> {}
class MyClass<T, U> : MyInterface<U> {}
class OtherClass<T, U> : MyInterface<IDictionary<U, T>> {}
Run Code Online (Sandbox Code Playgroud)
这种类型的接口可能有助于将类型与另一种类型相关联.
标记界面的另一个好用途是当你想要创建一种mixin时:
interface MyMixin {}
static class MyMixinMethods {
public static void Method(this MyMixin self) {}
}
class MyClass : MyMixin {
}
Run Code Online (Sandbox Code Playgroud)
在非周期访问者模式也使用它们.有时也使用术语"简并界面".
更新:
我不知道这个是否重要,但我已经用它们来标记后期编译器的类.
微软在制作.NET 1.0时没有严格遵循这些指导原则,因为指南与框架一起发展,并且一些规则在改变API时为时已晚.
IIRC,你提到的例子属于BCL 1.0,所以这可以解释它.
框架设计指南中对此进行了解释.
也就是说,该书还指出"[A]属性测试比类型检查要昂贵得多"(在Rico Mariani的附文中).
接着说,有时您需要标记接口进行编译时检查,这对于属性是不可能的.但是,我发现书中给出的例子(第88页)并不令人信服,所以我在此不再重复.
从性能角度来看:
由于反射,标记属性将比标记接口慢。如果不缓存反射,那么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 |
Run Code Online (Sandbox Code Playgroud)
但这并没有显着差异。
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>();
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5427 次 |
最近记录: |