2009-09-23 11 views
2

Un ancien Yet Another Language Geek blog post expliquant monades décrit l'ajout d'une méthode d'extension SelectMany à C# afin d'étendre la syntaxe linq à de nouveaux types. Je l'ai essayé en C# et cela fonctionne. J'ai fait une conversion directe à VB.net et ça ne marche pas. Est-ce que quelqu'un sait si VB.net supporte cette fonctionnalité ou comment l'utiliser?Ajout de C# SelectMany étend linq à un nouveau type monad, comment faire la même chose sur VB.net?

Voici le code C# qui fonctionne:

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; 
    } 
} 

Voici le code VB.net qui ne fonctionne pas (note: écrit en VS2010, donc peut manquer quelques continuités de ligne):

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 

Répondre

3

Apparemment, VB.net exige que, en plus de définir SelectMany, le type cible doit implémenter les méthodes que vous voulez (par exemple, Select, Where, etc.).

Ajouter cette méthode à l'identité et le programme compile et fonctionne:

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

Vous pouvez également mettre en œuvre comme une méthode d'extension pour "LINQ-IFY" types existants:

<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 

En outre, VB.net nécessite seulement SelectMany s'il y a plusieurs lignes 'from'. Si l'expression est de la forme "de x select x + 1" alors seulement l'identité.La méthode Select doit être implémentée.

2

Le code équivalent doit également être pris en charge par VB. Assurez-vous bien traduire les méthodes d'extension correctement:

Ce C#:

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

deviendrait cette 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 

Mise à jour: Votre code ci-dessus est correct, il semble que vous venez de courir dans une limitation du compilateur VB. Pour autant que je sache, ce que vous essayez de faire est légal selon les spécifications linguistiques.

Cependant, j'ai pu tromper le compilateur d'accepter la requête en ayant Identity(Of T) semblant de mettre en œuvre 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 

Une fois que nous convainquons le compilateur que c'est une requête valide, il résout correctement l'appel à votre personnalisé SelectMany.

Mise à jour 2: Ou oui, que Strilanc vous avez dit. J'ai essayé cela en premier mais j'ai apparemment oublié l'attribut Extension. De la spécification de langage, quelque chose est considéré comme interrogeable si, par ordre de préférence ...

  1. Il définit une méthode de sélection conforme.
  2. Il a une méthode AsEnumerable() ou AsQueryable().
  3. Il a une méthode Cast (Of T).
+0

J'ai inclus un exemple de code dans la question maintenant, ainsi vous pouvez vérifier si j'ai traduit correctement. –