创建可用作Func /方法的类型

时间:2013-03-22 08:55:03

标签: c# linq types expression

我一直在玩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);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

我创建了一个简单的测试平台&#39;原始代码: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;
        }
    }
}

正如您所看到的,它将您的课程定义为&#34; dynamic&#34;并允许您尝试调用它,就像它是委托/函数/ ...一般可调用:

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

}

答案 1 :(得分:1)

您无法将自定义成员添加到委托类型,也无法在C#中重载operator ()。这将为您提供扩展方法。

现在,您不希望向Func<int, int>等非常常的委托类型添加扩展,因为这会污染命名空间。我建议你创建这样的自定义代理:

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

然后,您可以向Z3Func添加扩展程序。

扩展调用将以您正在分析的表达式树中的静态方法调用结束。