2017-07-06 61 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 (任務))作爲任務...你的代碼...))'? –