没有使用generics扩展方法的类型推断
我有以下方法:
public static TEventInvocatorParameters Until (this TEventInvocatorParameters p, Func breakCond) where TEventInvocatorParameters : EventInvocatorParameters where TEventArgs : EventArgs { p.BreakCondition = breakCond; return p; }
而这堂课
public class EventInvocatorParameters where T : EventArgs { public Func BreakCondition { get; set; } // Other properties used below omitted for brevity. }
现在,我有以下问题:
- 此扩展方法显示所有类型,甚至是
string
。 - 我不能写
new EventInvocatorParameters(EventABC).Until(e => false);
它告诉我“方法的类型参数……不能从用法中推断出来。”
我不能像这样使用generics类型参数吗? 你会如何解决这个问题?
重要的一点:我需要这两个通用参数,因为我需要返回调用此扩展方法的相同类型。
更广泛的图片(没有必要回答问题!):
我正在尝试创建一个流畅的接口来调用事件。 基础是这个静态类:
public static class Fire { public static void Event( ConfiguredEventInvocatorParameters parameters) where TEventArgs : EventArgs { if (parameters.EventHandler == null) { return; } var sender = parameters.Sender; var eventArgs = parameters.EventArgs; var breakCondition = parameters.BreakCondition; foreach (EventHandler @delegate in parameters.EventHandler.GetInvocationList()) { try { @delegate(sender, eventArgs); if (breakCondition(eventArgs)) { break; } } catch (Exception e) { var exceptionHandler = parameters.ExceptionHandler; if (!exceptionHandler(e)) { throw; } } } } }
要确保只能使用完全配置的参数调用此方法,它只接受从EventInvocatorParameters
派生的EventInvocatorParameters
:
public class ConfiguredEventInvocatorParameters : EventInvocatorParameters where T : EventArgs { public ConfiguredEventInvocatorParameters( EventInvocatorParameters parameters, object sender, T eventArgs) : base(parameters) { EventArgs = eventArgs; Sender = sender; } public T EventArgs { get; private set; } public object Sender { get; private set; } }
以下是有效的电话:
Fire.Event(EventName.With(sender, eventArgs)); Fire.Event(EventName.With(sender, eventArgs).Until(e => e.Cancel)); Fire.Event(EventName.Until(e => e.Cancel).With(sender, eventArgs));
以下内容无效:
// no sender or eventArgs have been specified, ie missing call to With(...) Fire.Event(EventName.Until(e => e.Cancel));
为了使其工作,存在名为With
扩展方法,它接受EventHandler<TEventArgs
或TEventInvocatorParameters
并返回ConfiguredEventInvocatorParameters
。 现在, With
之后的所有调用也需要返回类型ConfiguredEventInvocatorParameters
,否则有效调用的第二个示例(结尾处的Until
)将不起作用。
如果您对API有任何想法,请告诉我。 但是,我想避免以下三件事:
- 如果尚未完全配置参数,则仅在运行时失败
- 创建一个反向语法,如
EventName.With(...).Until(...).Fire()
- 使用臭名昭着的
Do
方法开始:Fire(EventName).With(...).Until(...).Do();
有意地通用方法类型推断不会从约束中进行任何推断。 相反,从参数和forms参数中进行推导,然后根据约束检查推导出的类型参数。
有关约束和方法签名的一些设计问题的详细讨论,包括几十个人告诉我,我认为现有的设计是明智的是错误的,请参阅我关于这个主题的文章:
对于任何有兴趣的人,现在,我用通用的类层次结构解决了原始问题(流畅的事件调用API)。 这基本上是Hightechrider对类固醇的回答。
public abstract class EventInvocatorParametersBase where TEventArgs : EventArgs where TEventInvocatorParameters : EventInvocatorParametersBase { protected EventInvocatorParametersBase( EventHandler eventHandler, Func exceptionHandler, Func breakCondition) { EventHandler = eventHandler; ExceptionHandler = exceptionHandler; BreakCondition = breakCondition; } protected EventInvocatorParametersBase( EventHandler eventHandler) : this(eventHandler, e => false, e => false) { } public Func BreakCondition { get; set; } public EventHandler EventHandler { get; set; } public Func ExceptionHandler { get; set; } public TEventInvocatorParameters Until( Func breakCondition) { BreakCondition = breakCondition; return (TEventInvocatorParameters)this; } public TEventInvocatorParameters WithExceptionHandler( Func exceptionHandler) { ExceptionHandler = exceptionHandler; return (TEventInvocatorParameters)this; } public ConfiguredEventInvocatorParameters With( object sender, TEventArgs eventArgs) { return new ConfiguredEventInvocatorParameters ( EventHandler, ExceptionHandler, BreakCondition, sender, eventArgs); } } public class EventInvocatorParameters : EventInvocatorParametersBase, T> where T : EventArgs { public EventInvocatorParameters(EventHandler eventHandler) : base(eventHandler) { } } public class ConfiguredEventInvocatorParameters : EventInvocatorParametersBase, T> where T : EventArgs { public ConfiguredEventInvocatorParameters( EventHandler eventHandler, Func exceptionHandler, Func breakCondition, object sender, T eventArgs) : base(eventHandler, exceptionHandler, breakCondition) { EventArgs = eventArgs; Sender = sender; } public ConfiguredEventInvocatorParameters(EventHandler eventHandler, object sender, T eventArgs) : this(eventHandler, e => false, e => false, sender, eventArgs) { } public T EventArgs { get; private set; } public object Sender { get; private set; } } public static class EventExtensions { public static EventInvocatorParameters Until ( this EventHandler eventHandler, Func breakCondition) where TEventArgs : EventArgs { return new EventInvocatorParameters (eventHandler). Until(breakCondition); } public static EventInvocatorParameters WithExceptionHandler ( this EventHandler eventHandler, Func exceptionHandler) where TEventArgs : EventArgs { return new EventInvocatorParameters (eventHandler). WithExceptionHandler(exceptionHandler); } public static ConfiguredEventInvocatorParameters With ( this EventHandler eventHandler, object sender, TEventArgs eventArgs) where TEventArgs : EventArgs { return new ConfiguredEventInvocatorParameters ( eventHandler, sender, eventArgs); } }
这允许您编写如下代码:
Fire.Event(EventName.WithExceptionHandler(e => false) .Until(e => false).With(this, EventArgs.Empty)); Fire.Event(EventName.With(this, EventArgs.Empty)); Fire.Event(EventName.WithExceptionHandler(e => false) .With(this, EventArgs.Empty).Until(e => false)); Fire.Event(EventName.With(this, EventArgs.Empty) .WithExceptionHandler(e => false).Until(e => false));
但是它不允许你写这个,因为并没有提供所有必要的信息(eventArgs和sender):
Fire.Event(EventName.Until(e => false)); Fire.Event(EventName);
您是否需要使用扩展方法? 如果在EventInvocatorParameters
类上放置Until
,则可以避免上述两个问题:
public class EventInvocatorParameters where T : EventArgs { public Func BreakCondition { get; set; } // Other properties used below omitted for brevity. public EventInvocatorParameters Until (Func breakCond) { this.BreakCondition = breakCond; return this; } }
我知道一点警察,但你是否考虑过使用Rx ,而不是重新发明你想要做的事情?