没有使用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. } 

现在,我有以下问题:

  1. 此扩展方法显示所有类型,甚至是string
  2. 我不能写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<TEventArgsTEventInvocatorParameters并返回ConfiguredEventInvocatorParameters 。 现在, With之后的所有调用也需要返回类型ConfiguredEventInvocatorParameters ,否则有效调用的第二个示例(结尾处的Until )将不起作用。
如果您对API有任何想法,请告诉我。 但是,我想避免以下三件事:

  • 如果尚未完全配置参数,则仅在运行时失败
  • 创建一个反向语法,如EventName.With(...).Until(...).Fire()
  • 使用臭名昭着的Do方法开始: Fire(EventName).With(...).Until(...).Do();

有意地通用方法类型推断不会从约束中进行任何推断。 相反,从参数forms参数中进行推导,然后根据约束检查推导出的类型参数。

有关约束和方法签名的一些设计问题的详细讨论,包括几十个人告诉我,我认为现有的设计是明智的是错误的,请参阅我关于这个主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

对于任何有兴趣的人,现在,我用通用的类层次结构解决了原始问题(流畅的事件调用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 ,而不是重新发明你想要做的事情?