簡體   English   中英

創建可以用作功能/方法的類型

[英]Creating a type that can be used as a Func/method

我一直在玩LINQ to Z3娛樂(不是用於生產)。

我從以下語法開始:

var i = 123;
var test2 = from t in TheormProver.NewTheorm()
            let f = TheormProver.Func<int, bool>()
            let a = TheormProver.Int
            let g = TheormProver.Func<bool, int>()
            where !f(a * 2) && g(f(g(f(4)))) == i * a && a < g(f(a))
            select new { f = f.ToString(), g = g.ToString(), a, asd = "Test extra property" };

var solution = test2.Solve(); // Edited in for clarification
// note that test2 is a TheormProver<T> which has a "T Solve()" method defined.

靜態TheromProver.IntTheormProver.Func方法/屬性當前僅返回基本類型(按其名稱)。

往前走,我想創建一種Variable<T>類型,該類型不僅包含值,還包含更多信息。

TL; DR:我遇到的問題是我希望fg變量是可以向其添加字段和屬性的自定義類型, 但是我仍然希望能夠將它們與所使用的語法一起使用where子句(即作為方法/ Func )。

因此, 如何在添加/擁有自己的屬性時創建可用於方法語法的自定義類型?

請注意,我不在乎調用該方法是否無效,或者不起作用,因為我將處理where子句,因此它們永遠不會被調用/執行。


例:

var test2 = from t in TheormProver.NewTheorm()
            let f = TheormProver.Func<int, bool>()
            let a = TheormProver.Int
            where !f(a * 2) && a > 3 // f is used to create a method call expression
            select new { f , a };

var testSolution = test2.Solve();

var fSolution = testSolution.f; // F is its own type with unique properties/fields.

var fConstraints = fSolution.Constraints;

var fSomeProperty = fSolution.SomeProperty;

foreach(var constraint in fConstraints)
{
    //.....
}

我已經模擬了到目前為止到目前為止進行中的語法的一個簡單示例:

http://liveworkspace.org/code/3Fm6JM$0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication1
{
    class TheormProver
    {
        public static int Int { get { return default(int); } } // Really this would return my Variable<int>
        public static Func<T, TResult> Func<T, TResult>() { return default(Func<T, TResult>); } // Really this would return my Variable<Func<T, TResult>>

        protected List<Expression> Constraints; // Holds constraints / where clauses that get translated into the Z3 language

        //This gets called when we do the first "let" and gets us into the correctly typed world with a generic parameter
        public virtual TheormProver<T> Select<T>(Func<TheormProver, T> sel)
        {
            return new TheormProver<T>(Constraints);
        }
    }

    // This is what the user of the library sees and is returned by a from t in new TheormProver(). T will be the anonymous type from the last let
    class TheormProver<T> : TheormProver
    {
        public TheormProver(List<Expression> Constraints)
        {

        }

        // This gets called on subsequent "let"s, going from the anonymous type with one property "f" to one with 2, "f, g". Chaining this way allows as many lets as we want
        public virtual TheormProver<U> Select<U>(Expression<Func<T, U>> sel)
        {
            return new TheormProver<T, U>(sel, Constraints.ToList());
        }

        public virtual TheormProver<T> Where(Expression<Func<T, bool>> constraint)
        {
            var result = (TheormProver<T>)this; // This should be a clone to allow composable queries

            result.Constraints.Add(constraint);

            return result;
        }

        public virtual T Solve(out bool foundSolution)
        {
            // TODO: Call Z3 and get a solution
            foundSolution = false;
            return default(T);
        }
    }

    internal class TheormProver<T, U> : TheormProver<U>
    {
        private LambdaExpression Selector;
        private TheormProver<T> InternalTheorumProver;

        public TheormProver(Expression<Func<T, U>> selector, List<Expression> constraints)
            : base(constraints)
        {
            Selector = selector;
            InternalTheorumProver = new TheormProver<T>(constraints);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var test = from t in new TheormProver()
                       let f = TheormProver.Func<int, bool>()
                       let g = TheormProver.Func<bool, int>()
                       let a = TheormProver.Int
                       where g(f(a)) == 0
                       select new { f, g, a };

            bool foundSolution;
            var testSolution = test.Solve(out foundSolution);
        }
    }
}

我已經為您的原始代碼創建了一個簡單的“測試平台”: http : //liveworkspace.org/code/3Bl7wC$0

使用一些動態魔術,您可以擁有以下課程來代替Func<T1, T2>

public class MyCallable<T1, T2> : DynamicObject
{
    private readonly Expression<Func<T1, T2> > _wrapped;
    private readonly Func<T1, T2> _compiled;

    public MyCallable(Expression<Func<T1, T2>> towrap) 
    { 
        _wrapped = towrap; _compiled = _wrapped.Compile(); 
    }

    public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
    {
        if ( (args.Length == 1) && 
             (args[0].GetType() == typeof(T1)))
        {
            Console.WriteLine(@"Invoking ""{0}"" on {1}", _wrapped, args[0]);
            result = _compiled((T1) args[0]);
            return true;
        }
        else
        {
            //throw new ArgumentException("Cannot invoke " + _wrapped + " with the arguments passed");
            result = null;
            return false;
        }
    }
}

如您所見,它將您的類定義為“動態的”,並允許您像調用委托/函數/ ...那樣嘗試並調用它:一般可調用的:

// in "TheormProver"
public static dynamic Func<T1, T2>() { return new MyCallable<T1, T2>(arg1 => default(T2)); }

這是可行的證明: http : //liveworkspace.org/code/4kBypd$0

輸出:

Invoking "arg1 => False" on 0
Invoking "arg1 => False" on 4
Invoking "arg1 => 0" on False
Invoking "arg1 => False" on 0
Invoking "arg1 => 0" on False
Invoking "arg1 => False" on 0
Invoking "arg1 => 0" on False

完整代碼供參考:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Dynamic;

public class Program
{
    public class MyCallable<T1, T2> : DynamicObject
    {
        private readonly Expression<Func<T1, T2> > _wrapped;
        private readonly Func<T1, T2> _compiled;

        public MyCallable(Expression<Func<T1, T2>> towrap) 
        { 
            _wrapped = towrap; _compiled = _wrapped.Compile(); 
        }

        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
        {
            if ( (args.Length == 1) && 
                 (args[0].GetType() == typeof(T1)))
            {
                Console.WriteLine(@"Invoking ""{0}"" on {1}", _wrapped, args[0]);
                result = _compiled((T1) args[0]);
                return true;
            }
            else
            {
                //throw new ArgumentException("Cannot invoke " + _wrapped + " with the arguments passed");
                result = null;
                return false;
            }
        }
    }

    public static class TheormProver
    {
        public static object[] NewTheorm() { return new object[] { 1 }; }
        public static dynamic Func<T1, T2>() { return new MyCallable<T1, T2>(arg1 => default(T2)); }
        public static int Int { get; set; }
    }

    public static void Main(string[] args)
    {
        var i = 123;
        var test2 = from t in TheormProver.NewTheorm()
            let f = TheormProver.Func<int, bool>()
            let a = TheormProver.Int
            let g = TheormProver.Func<bool, int>()
            where !f(a * 2) && g(f(g(f(4)))) == i * a && a < g(f(a))
            select new { f = f.ToString(), g = g.ToString(), a, asd = "Test extra property" };

        test2.ToList().ForEach(Console.WriteLine);
    }

}

您不能將自定義成員添加到委托類型,也不能在C#中重載operator () 剩下的就是擴展方法。

現在,您不想向諸如Func<int, int>這樣的通用委托類型添加擴展Func<int, int>因為這會污染名稱空間。 我建議您創建這樣的自定義委托:

delegate TResult Z3Func<T1, TResult>(T1 arg1);

然后,您可以將擴展添加到Z3Func

擴展調用最終將作為您正在分析的表達式樹中的靜態方法調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM