通用扩展方法没有类型推断
我有以下方法:
public static TEventInvocatorParameters Until
<TEventInvocatorParameters, TEventArgs>(this TEventInvocatorParameters p,
Func<TEventArgs, bool> breakCond)
where TEventInvocatorParameters : EventInvocatorParameters<TEventArgs>
where TEventArgs : EventArgs
{
p.BreakCondition = breakCond;
return p;
}
这个类
public class EventInvocatorParameters<T>
where T : EventArgs
{
public Func<T, bool> BreakCondition { get; set; }
// Other properties used below omitted for brevity.
}
现在,我有以下问题:
- 此扩展方法显示在所有类型上,甚至
string
上。 - 我无法编写
new EventInvocatorParameters
它告诉我“方法的类型参数...无法从用法中推断出来。 ”(EventABC).Until(e => false);
我不能使用这样的泛型类型参数吗?您将如何解决这个问题?
重要的一点:我需要这两个泛型参数,因为我需要返回调用此扩展方法的相同类型。
更广泛的图片(回答问题不需要!):
我正在尝试创建一个调用事件的流畅界面。基础是这个静态类:
public static class Fire
{
public static void Event<TEventArgs>(
ConfiguredEventInvocatorParameters<TEventArgs> parameters)
where TEventArgs : EventArgs
{
if (parameters.EventHandler == null)
{
return;
}
var sender = parameters.Sender;
var eventArgs = parameters.EventArgs;
var breakCondition = parameters.BreakCondition;
foreach (EventHandler<TEventArgs> @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
的 ConfiguredEventInvocatorParameters
:
public class ConfiguredEventInvocatorParameters<T>
: EventInvocatorParameters<T>
where T : EventArgs
{
public ConfiguredEventInvocatorParameters(
EventInvocatorParameters<T> 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, i.e. missing call to With(...)
Fire.Event(EventName.Until(e => e.Cancel));
为了使这项工作正常进行,存在名为 With
的扩展方法,它接受EventHandler
TEventInvocatorParameters
并返回 ConfiguredEventInvocatorParameters
。 With
之后的所有调用现在还需要返回类型 ConfiguredEventInvocatorParameters
,否则是有效调用的第二个示例(使用 Until
最后)行不通。
如果您对 API 有任何总体想法,请告诉我。但是,我想避免以下三件事:
- 如果参数尚未完全配置,则仅在运行时失败
- 创建类似
EventName.With(...).Until(...).Fire() 的反向语法
- 使用臭名昭著的
Do
方法来开始:Fire(EventName).With(...).Until(...).Do();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
2020 年 11 月更新:以下原始答案写于 2011 年;泛型方法类型推断、重载解析以及方法的“最终验证”如何完成的规则在最新版本的 C# 中发生了微小但重大的变化;这个答案以及我原来的 MSDN 博客上有关它的存档文章的链接可能不再准确。另外,微软出于法律原因删除了原文章的评论;这些评论中有大量的背景和讨论。我希望在某个时候有时间重新审视这篇文章,以澄清(1)今天的规则,(2)它们是如何变化的,以及(3)那些已删除评论中讨论的想法如何影响这些决定,但这已经很多了我可能有一段时间无法完成工作。请记住,自 2012 年 11 月以来,我就不再加入 C# 语言设计团队。
泛型方法类型推断故意不从约束中进行任何推导。相反,从参数和形式参数进行推导,然后根据约束检查推导的类型参数。
有关约束和方法签名的一些设计问题的详细讨论,包括几十个人告诉我,我错误地认为现有的设计是明智的,请参阅我关于该主题的文章:
https://learn.microsoft.com/en-gb/archive/blogs/ericlippert/constraints-are-not-part-of-the-signature
UPDATE from November 2020: The original answer below was written in 2011; the rules for generic method type inference, overload resolution, and how "final validation" of methods is done have had small but significant changes in recent versions of C#; this answer, and the link to an archived article on my original MSDN blog about it might no longer be accurate. Also, Microsoft deleted the comments on the original article for legal reasons; there was a huge amount of context and discussion in those comments. I hope to at some point have the time to revisit this article to clarify (1) the rules today, (2) how they have changed, and (3) how the ideas discussed in those deleted comments influenced those decisions, but that's a lot of work and I may not get to it for some time. Remember, I have not been on the C# language design team since November 2012.
Generic method type inference deliberately does not make any deductions from the constraints. Rather, deductions are made from the arguments and the formal parameters, and then the deduced type arguments are checked against the constraints.
For a detailed discussion of some of the design issues around constraints and method signatures, including several dozen people telling me that I'm wrong to think that the existing design is sensible, see my article on the subject:
https://learn.microsoft.com/en-gb/archive/blogs/ericlippert/constraints-are-not-part-of-the-signature
对于任何感兴趣的人,目前,我使用通用类层次结构解决了最初的问题(流畅的事件调用 API)。这基本上就是 Hightechrider 对类固醇的回答。
这允许您编写如下代码:
但它不允许您编写此代码,因为并未提供所有必要的信息(eventArgs 和发件人):
For anyone interested, for now, I solved the original problem (fluent event invocation API) with a generic class hierarchy. This is basically Hightechrider's answer on steroids.
This allows you to write code like this:
But it doesn't allow you to write this, because not all necessary info (eventArgs and sender) has been provided:
您是否有需要使用扩展方法的原因?如果将
Until
放在EventInvocatorParameters
类上,则可以避免上述两个问题:Is there some reason you need to use an extension method? If you put
Until
on theEventInvocatorParameters<T>
class you can avoid both of the problems mentioned:我知道有点逃避,但是您是否考虑过使用 Rx 来代替,而不是重新发明你似乎正在尝试做的事情?
Bit of a cop-out I know, but have you considered using Rx instead, rather than re-inventing what you appear to be trying to do?