In C # aggiungendo SelectMany estende LINQ a un nuovo tipo monade, come faccio a fare la stessa cosa in VB.net?
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
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 ...
- Si definisce un metodo Select conforme. Metodo
- Ha un AsEnumerable () o AsQueryable ().
- Ha un Cast (Of T) metodo.