En C# Agregar selectmany extiende Linq a un nuevo tipo de mónada, ¿cómo hago lo mismo en vb.net?
Pregunta
Un viejo Otra publicación de blog de geek de idiomas Explicar las monads describe agregar un método de extensión SelectMany a C# para extender la sintaxis LINQ a nuevos tipos.
Lo he probado en C# y funciona. Hice una conversión directa a VB.NET y no funciona. ¿Alguien sabe si VB.NET admite esta función o cómo usarla?
Aquí está el código C# que funciona:
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;
}
}
Aquí está el código VB.NET que no funciona (nota: escrito en VS2010, por lo que puede faltar algunas continuaciones de línea):
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
Solución
Aparentemente, VB.NET requiere que, además de definir SelectMany, el tipo de destino debe implementar los métodos que desee (por ejemplo, seleccione, dónde, etc.).
Agregue este método a la identidad y el programa compila y funciona:
Public Function [Select](Of R)(ByVal projection As Func(Of T, R)) As Identity(Of R)
Return projection(value).ToIdentity
End Function
También puede implementarlo como un método de extensión para "LINQ-IFY" tipos existentes:
<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
Además, VB.NET solo requiere SelectMany si hay múltiples líneas 'de'. Si la expresión es del formulario "de x seleccione x+1", solo debe implementarse el método identity.select.
Otros consejos
El código equivalente también debe ser compatible con VB. Asegúrese de traducir correctamente los métodos de extensión correctamente:
Este C#:
public static class MonadExtensions
{
public static Identity<T> ToIdentity<T>(this T value)
{
return new Identity<T>(value);
}
}
Se convertiría en este 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
Actualizar: Su código anterior es correcto, por lo que parece que solo se encuentra con una limitación del compilador VB. Por lo que puedo decir, lo que estás tratando de hacer es legal de acuerdo con las especificaciones del idioma.
Sin embargo, pude engañar al compilador para que aceptara la consulta al tener Identity(Of T)
fingir implementar 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 vez que convencemos al compilador de que es una consulta válida, resuelve correctamente la llamada a su costumbre SelectMany
.
Actualización 2: O si, que Estriloso Usted dijo. Lo intenté primero pero aparentemente olvidé el Extension
atributo. De la especificación del idioma, algo se considera consultable si, en orden de preferencia ...
- Define un método de selección conforme.
- Tiene un método asenumerable () o asquerable ().
- Tiene un método fundido (de t).