2009-09-23 6 views
2

Старая Yet Another Language Geek blog post объясняющая монады описывает добавление метода расширения SelectMany к C#, чтобы расширить синтаксис linq для новых типов.В добавлении C# SelectMany расширяет linq до нового типа монады, как мне сделать то же самое в VB.net?

Я пробовал это на C#, и он работает. Я сделал прямое преобразование в VB.net, и он не работает. Кто-нибудь знает, поддерживает ли VB.net эту функцию или как ее использовать?

Здесь C# код, который работает:

class Identity<T> { 
    public readonly T Value; 
    public Identity(T value) { this.Value = value; } 
} 
static class MonadExtension { 
    public static Identity<T> ToIdentity<T>(this T value) { 
     return new Identity<T>(value); 
    } 
    public static Identity<V> SelectMany<T, U, V>(this Identity<T> id, Func<T, Identity<U>> k, Func<T, U, V> s) { 
     return s(id.Value, k(id.Value).Value).ToIdentity(); 
    } 
} 
class Program { 
    static void Main(string[] args) { 
     var r = from x in 5.ToIdentity() 
       from y in 6.ToIdentity() 
       select x + y; 
    } 
} 

Вот код VB.net, который не работает (примечание: написано в VS2010, поэтому может отсутствовать некоторые продолжения строки):

Imports System.Runtime.CompilerServices 

Public Class Identity(Of T) 
    Public ReadOnly value As T 
    Public Sub New(ByVal value As T) 
     Me.value = value 
    End Sub 
End Class 
Module MonadExtensions 
    <Extension()> _ 
    Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T) 
     Return New Identity(Of T)(value) 
    End Function 
    <Extension()> _ 
    Public Function SelectMany(Of T, U, V)(ByVal id As Identity(Of T), ByVal k As Func(Of T, Identity(Of U)), ByVal s As Func(Of T, U, V)) As Identity(Of V) 
     Return s(id.value, k(id.value).value).ToIdentity() 
    End Function 
End Module 
Public Module MonadTest 
    Public Sub Main() 
     ''Error: Expression of type 'Identity(Of Integer)' is not queryable. 
     Dim r = From x In 5.ToIdentity() _ 
       From y In 6.ToIdentity() _ 
       Select x + y 
    End Sub 
End Module 

ответ

3

Очевидно, что VB.net, помимо определения SelectMany, должен реализовать требуемые методы (например, Select, Where и т. Д.).

Добавьте этот метод Идентичность и программа компилируется и работает:

Public Function [Select](Of R)(ByVal projection As Func(Of T, R)) As Identity(Of R) 
    Return projection(value).ToIdentity 
End Function 

Вы также можете реализовать его в качестве метода расширения для «LINQ-римента» существующие типы:

<Extension()> _ 
Public Function [Select](Of T, R)(ByVal this As Identity(Of T), ByVal projection As Func(Of T, R)) As Identity(Of R) 
    Return projection(this.value).ToIdentity 
End Function 

Кроме того, VB.net требует только SelectMany, если имеется несколько строк «from». Если выражение имеет вид «from x select x + 1», то только Identity.Выбор метода должен быть реализован.

2

Эквивалентный код также должен поддерживаться VB. Убедитесь, что вы правильно неправильно транслируют методы расширения:

Это C#:

public static class MonadExtensions 
{ 
    public static Identity<T> ToIdentity<T>(this T value) 
    { 
     return new Identity<T>(value); 
    } 
} 

бы стать этим VB:

Imports System.Runtime.CompilerServices 

Module MonadExtensions 

    <Extension()> _ 
    Public Function ToIdentity(Of T)(ByVal value As T) As Identity(Of T) 
    Return New Identity(Of T)(value) 
    End Function 

End Module 

Update: Ваш код выше правильно, так это похоже, вы просто сталкиваетесь с ограничением компилятора VB. Насколько я могу судить, то, что вы пытаетесь сделать, является законным в соответствии со спецификацией языка.

Однако, я был в состоянии обмануть компилятор в принятии запроса, имея Identity(Of T) претендует на реализацию IEnumerable(Of T):

Public Class Identity(Of T) 
    Implements IEnumerable(Of T) 
    Public ReadOnly value As T 
    Public Sub New(ByVal value As T) 
    Me.value = value 
    End Sub 

    Public Function GetEnumerator() As IEnumerator(Of T) _ 
    Implements IEnumerable(Of T).GetEnumerator 

    Throw New InvalidOperationException("This should never be called.") 
    End Function 
    Public Function GetEnumerator1() As IEnumerator _ 
    Implements IEnumerable(Of T).GetEnumerator 

    Throw New InvalidOperationException("This should never be called.") 
    End Function 
End Class 

После того, как мы убедить компилятор, что действительный запрос, он правильно решает вызов на изготовленный под заказ SelectMany.

Обновление 2: Или да, что Strilanc Вы сказали. Сначала я попытался, но, по-видимому, забыл атрибут Extension. Из спецификации языка что-то считается запрошенным, если в порядке предпочтения ...

  1. Он определяет соответствующий метод выбора.
  2. У него есть метод AsEnumerable() или AsQueryable().
  3. У него есть метод Cast (Of T).
+0

Теперь я включил пример кода в вопрос, чтобы вы могли проверить, правильно ли я перевел. –