C#之动态语言扩展


DLR

在.NET Framework中,DLR2位于System.Dynamic命名空间和System.Runtime.CompilerServices命名空间的几个类中。

dynamic 类型

可以发现staticPerson出现了编译错误,而dynamicPerson并没有,因为定义为dynamic的对象可以在运行期改变其类型,甚至改变多次,这与强制转换类型是不一样的,它在编译期不会做检查。

对于dynamic 类型有两个限制:动态对象不支持扩展方法,匿名函数(lambda表达式)也不能用于动态方法调用参数,因此LINQ不能用于动态对象。大多数LINQ调用都是扩展方法,而lambda表达式用作这些扩展方法的参数。

包含DLR ScriptRuntime

项目通过NuGet安装IronPython,然后using Microsoft.Scripting.Hosting,就有了ScriptRuntime

就可以执行存储在文件中 的代码段或完整的脚本。

例:

xaml文件


    
        
            
            
            
            
            
            
            
            
        

        Disc Based on Cost
        Disc Based on No of Items
        
            Total No of Items:
            
        
        
            Total Amount
            
        
        
            
using Microsoft.Scripting.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DiscountWPF
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

       
        private void calDisc_Click(object sender, RoutedEventArgs e)
        {
            string scriptToUse;
            if (CostRadioButton.IsChecked.Value)
            {
                scriptToUse = "AmountDisc.py";
            }
            else
            {
                scriptToUse = "CountDisc.py";
            }
            ScriptRuntime scriptRuntime = ScriptRuntime.CreateFromConfiguration();
            ScriptEngine pythEng = scriptRuntime.GetEngine("python");
            ScriptSource source = pythEng.CreateScriptSourceFromFile(scriptToUse);
            ScriptScope scope = pythEng.CreateScope();
            scope.SetVariable("prodCount", Convert.ToInt32(totalItems.Text));
            scope.SetVariable("amt", Convert.ToDecimal(totalAmount.Text));
            source.Execute(scope);
            label.Text = scope.GetVariable("retAmt").ToString();
        }

        private void calTax_Click(object sender, RoutedEventArgs e)
        {
            ScriptRuntime scriptRuntime = ScriptRuntime.CreateFromConfiguration();
            dynamic calcRatet = scriptRuntime.UseFile("CalcTax.py");
            labelA.Text = calcRatet.CalcTax(Convert.ToDecimal(label.Text)).ToString();
        }
    }
}

从代码可知,ScriptRuntime对象是通过配置文件来产生的,因为调用了ScriptRuntime.CreateFromConfiguration()方法产生的,因此,本项目的App.config如下:

<?xml version="1.0" encoding="utf-8" ?>

	
		

Calc Discount按钮的click事件调用的方法是通过依次创建ScriptRuntimeScriptEngineScriptSource,ScriptScope对象,对ScriptSource进行执行,从ScriptScope设定变量的值,获取变量的值。

Cal Tax按钮的click事件调用的 方法是创建ScriptRuntime,然后通过该对象的UseFile方法,得到Dynamic类型对象,然后通过该动态对象,调用脚本中的方法。

CountDisc.py

discCount=5 
discAmt=0.1
retAmt=amt 
if(prodCount>discCount):
    retAmt=amt-(amt*discAmt)

AmountDisc.py

discAmt=0.25 
retAmt=amt
if amt>25:
    retAmt=amt-(amt*discAmt)

CalTax.py

def CalcTax(amount):
    return amount*1.075 

DynamicObject和ExpandoObject

  • DynamicObject

创建自己的动态对象,要么从DynamicObject中派生,也要么使用ExpandoObject

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DynamicDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic wroxDyn = new WroxDynamicObject();
            wroxDyn.FirstName = "John";
            wroxDyn.LastName = "Yang";
            Console.WriteLine(wroxDyn.GetType());
            Console.WriteLine("{0}--{1}", wroxDyn.FirstName, wroxDyn.LastName);
            Func GetTomo = today => today.AddDays(1).ToShortDateString();
            wroxDyn.GetTomorrow = GetTomo;
            Console.WriteLine("Tomorrow is {0}", wroxDyn.GetTomorrow(DateTime.Now));
        }
    }
    class WroxDynamicObject : DynamicObject
    {
        Dictionary _dynamicData = new Dictionary();
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            bool success = false;
            result = null;
            if (_dynamicData.ContainsKey(binder.Name))
            {
                result = _dynamicData[binder.Name];
                success = true;
            }
            else
            {
                result = "Property not found";

            }
            return success;
        }
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            _dynamicData[binder.Name] = value;
            return true;
        }
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            dynamic method = _dynamicData[binder.Name];
            result = method((DateTime)args[0]);
            return result != null;
        }
    }
    

}

output

DynamicDemo.WroxDynamicObject
John--Yang
Tomorrow is 2022/2/5
  • ExpandoObject

ExpandoObject不用重写方法,可以直接使用,如果需要控制动态对象中属性的添加和访问,则使用DynamicObject是最佳选择,其他情况,则使用dynamic或者ExpandoObject。

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DynamicDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic wroxDyn = new ExpandoObject();
            wroxDyn.FirstName = "John";
            wroxDyn.LastName = "Yang";
            Console.WriteLine(wroxDyn.GetType());
            Console.WriteLine("{0}--{1}", wroxDyn.FirstName, wroxDyn.LastName);
            Func GetTomo = today => today.AddDays(1).ToShortDateString();
            wroxDyn.GetTomorrow = GetTomo;
            Console.WriteLine("Tomorrow is {0}", wroxDyn.GetTomorrow(DateTime.Now));
        }
    }
}

output

System.Dynamic.ExpandoObject
John--Yang
Tomorrow is 2022/2/5

利用ExpandoObject,来储存任意数据类型的数据的一个Demo:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DynamicDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var retList = new List();
            dynamic expObj=new ExpandoObject();
            ((IDictionary)expObj).Add("john", 10);
            retList.Add(expObj);
            dynamic expObj1 = new ExpandoObject();
            ((IDictionary)expObj1).Add("yang", DateTime.Now);
            retList.Add(expObj1);
            foreach(var dy in retList)
            {
                var tempDic = (IDictionary)dy;
                foreach(var kv in tempDic)
                {
                    Console.WriteLine(kv.Key);
                    Console.WriteLine(kv.Value);
                }
            }



        }
    }

    

}

output

john
10
yang
2022/2/4 23:35:21
C