2017-07-06 63 views
10

所以我在C#下面的代码:C#函数使用扩展函数,但VB等效不?

public Container ConfigureSimpleInjector(IAppBuilder app) 
{ 
    var container = new Container(); 

    container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); 

    container.RegisterPackages(); 

    app.Use(async (context, next) => 
    { 
     using (AsyncScopedLifestyle.BeginScope(container)) 
     { 
      await next(); 
     } 
    }); 

    container.Verify(); 

    return container; 
} 

app.Use()被定义为Owin.AppBuilderUserExtensions.Use(),看起来像这样:

public static IAppBuilder Use(this IAppBuilder app, Func<IOwinContext, Func<Task>, Task> handler);

VB的等效如下:

Public Function ConfigureSimpleInjector(app As IAppBuilder) As Container 
    Dim container = New Container() 

    container.Options.DefaultScopedLifestyle = New AsyncScopedLifestyle() 

    container.RegisterPackages() 

    app.Use(Async Sub(context, [next]) 
       Using AsyncScopedLifestyle.BeginScope(container) 
        Await [next]() 
       End Using 
      End Sub) 

    container.Verify() 

    Return container 
End Function 

出于某种原因,在VB版本中,app.Use()不使用扩展功能可用,并且简单地使用IAppBuilder.Use()代替,如下:

Function Use(middleware As Object, ParamArray args() As Object) As IAppBuilder

为什么VB代码不使用C#做同样的扩展功能,我怎样才能使它使用?

编辑

为了清楚起见,扩展方法不是我自己的。他们来自欧文第三方图书馆。所以没有重命名扩展方法的选项。

+1

如果你在app.Use(Async ...)中使用'Func'而不是'Sub',它会有所作为吗?该方法的签名与框架中的不匹配 – pasty

+0

我不相信,因为据我所知,它不应该返回一个值。我的意思是使用'Function'而不是抛出一个错误,因为那么它的希望得到一个返回值 –

+2

IMO它应该为了使用一个重写方法,签名必须匹配,看一下[定义](https://msdn.microsoft.com/en-us/library/owin。 appbuilderuseextensions.use(v = vs.113).aspx?cs-save-lang = 1&cs-lang = vb#code-snippet-1)。如果没有,编译器应该如何知道,你想调用这个方法?目前来说吧看作一个参数(因此使用第一个方法定义)。你现在(你可以验证)在C#版本中,它使用Func参数调用方法? – pasty

回答

4

在VB(fiddle),可以传递需要的对象时lambda表达式:

Public Shared Sub Main() 
    MyMethod(Function() True) ' compiles 
End Sub 

Public Shared Sub MyMethod(o As Object) 
End Sub 

在C#(fiddle),即不工作:

public static void Main() 
{ 
    // Compilation error: Cannot convert lambda expression to type 'object' because it is not a delegate type. 
    // You'd need to use: MyMethod(new Func<bool>(() => true)); 
    MyMethod(() => true); 
} 

public static void MyMethod(object o) { } 

这是可能是由于lambda表达式可以隐式转换为VB中的代表的事实but not in C#

' Compiles 
Dim d As System.Delegate = Function() True 
// Compilation error: Cannot convert lambda expression to type 'Delegate' because it is not a delegate type 
System.Delegate d =() => true; 

因此,在你的代码示例中,VB找到一个匹配的合适的实例方法并使用它。在C#代码中,没有找到匹配的实例方法(因为您的lambda表达式不匹配object),而是使用扩展方法。

请注意,均为如果找到匹配方法,则语言更喜欢实例方法而不是扩展方法。这个can lead to subtle bugs如果稍后添加匹配的实例方法。


如何解决这个问题?我建议为扩展方法和实例方法使用不同的名称 - 其他开发人员在将来阅读您的代码时会感谢您的支持。如果这不是一个选项,你总是可以调用扩展方法:

Owin.AppBuilderUserExtensions.Use(app, Async Sub(context, [next]) ...) 
+1

感谢您更正我的(现已删除的)答案。我认为你在这里有正确的想法。 lambda表达式似乎不能隐式转换为C#中的'object'。值得一提的是,'Func' *可以隐式转换,但当然lambda表达式不是'Func'。在你的例子中,如果你用'MyMethod(新函数(()=> true)'替换了'MyMethod((=)=> true)',那么它会编译,并且它更喜欢实例级别的'MyMethod'即使扩展名使用了'Func '而不是'对象',也可以使用扩展方法。 –

+0

@JoeFarrell:没错。我认为这是因为lambda可以隐式转换为VB中的委托而不是C#。我在答案中添加了一个简短的例子。 – Heinzi

+0

扩展方法来自Owin第三方库。所以显然重命名不是一种选择。我如何明确地调用扩展方法?我不能做'IAppBuilder.Use(...)' –

5

在C#代码,因为next是FUNC键,awaitreturns a task,没有return语句,但是actualy返回一个Task对象。

IMO的VB.NET代码返回Task对象,因为拉姆达(在C#= Action)指定为Sub

在VB.NET代码中将Sub更改为Func应该调用想要的方法。

+1

@JunKang:试试这个:'Async Function(context as IOwinContext,[next] As Func(Of Task))As Task' –

+1

你可以尝试使用lambda来创建一个带有匹配签名的私有函数并将其传递给它作为参数。 – pasty

+0

我能想到的最后一个建议......这个非常明确的方法呢:app.Use(IOwinContext,Func(Of Task),Task)(Async Function(context As IOwinContext,[next] As Func (任务))作为任务...你的代码...))'? –