In C # aggiungendo SelectMany estende LINQ a un nuovo tipo monade, come faccio a fare la stessa cosa in VB.net?

StackOverflow https://stackoverflow.com/questions/1467293

  •  13-09-2019
  •  | 
  •  

Domanda

Un vecchio Ancora un'altra lingua Geek post monadi che spiegano descrive l'aggiunta di un metodo di estensione SelectMany a C # al fine di estendere la sintassi LINQ a nuovi tipi.

L'ho provato in C # e funziona. Ho fatto una conversione direttamente a VB.net e non funziona. Qualcuno sa se VB.net supporta questa funzione o come usarlo?

Ecco il codice # C che funziona:

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

Ecco il codice VB.net che non funziona (nota: scritto in VS2010, quindi potrebbero mancare alcuni continuazione di una riga):

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
È stato utile?

Soluzione

A quanto pare VB.net richiede che, oltre a definire SelectMany, il tipo di destinazione deve implementare i metodi che si desidera (ad es. Selezione, Dove, ecc).

Aggiungi questo metodo per identità e il programma viene compilato e funziona:

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

È anche possibile implementare come un metodo di estensione a "LINQ Ify" tipi esistenti:

<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

Inoltre, VB.net richiede solo SelectMany se ci sono più 'da' linee. Se l'espressione è del tipo "da selezionare x x + 1", allora solo il metodo Identity.Select necessario attuare.

Altri suggerimenti

Il codice equivalente dovrebbe essere sostenuto da VB pure. Assicurati di essere adeguatamente tradurre correttamente i metodi di estensione:

Questa C #:

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

sarebbe diventato questo 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

Aggiornamento: Il codice di cui sopra è corretto, così sembra che stai solo in esecuzione in una limitazione del compilatore VB. Per quanto posso dire, quello che stai cercando di fare è legale secondo le specifiche della lingua.

Comunque, sono stato in grado di ingannare il compilatore ad accettare la query avendo Identity(Of T) finta di implementare 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

Una volta che ci convinciamo il compilatore che si tratta di una query valida, si risolve correttamente la chiamata al vostro SelectMany personalizzato.

Aggiornamento 2: o sì, che cosa Strilanc hai detto. Ci ho provato prima, ma a quanto pare dimenticato l'attributo Extension. Dal spec lingua, qualcosa è considerato queryable se, in ordine di preferenza ...

  1. Si definisce un metodo Select conforme.
  2. Metodo
  3. Ha un AsEnumerable () o AsQueryable ().
  4. Ha un Cast (Of T) metodo.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top