2

モナドを説明するYet Another Language Geekの古いブログ投稿では、linq構文を新しい型に拡張するためにC#にSelectMany拡張メソッドを追加する方法について説明しています。

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
4

2 に答える 2

3

どうやら VB.net では、SelectMany の定義に加えて、ターゲット タイプが必要なメソッド (Select、Where など) を実装する必要があるようです。

このメソッドを Identity に追加すると、プログラムがコンパイルされて機能します。

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

既存の型を「linq-ify」する拡張メソッドとして実装することもできます。

<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

また、複数の「from」行がある場合、VB.net は SelectMany のみを必要とします。式が「from x select x+1」の形式の場合、Identity.Select メソッドのみを実装する必要があります。

于 2009-09-23T19:01:46.863 に答える
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

更新:上記のコードは正しいので、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. 準拠するSelectメソッドを定義します。
  2. AsEnumerable()またはAsQueryable()メソッドがあります。
  3. Cast(Of T)メソッドがあります。
于 2009-09-23T17:06:50.227 に答える