• 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文件

    <Window x:Class="DiscountWPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:DiscountWPF"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="300">
        <Grid AutomationProperties.HelpText="disc based on cost">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    
            <RadioButton x:Name="CostRadioButton" Grid.Row="0" VerticalAlignment="Center" >Disc Based on Cost</RadioButton>
            <RadioButton x:Name="NumRadioButton" Grid.Row="1" VerticalAlignment="Center" >Disc Based on No of Items</RadioButton>
            <StackPanel Grid.Row="2" Orientation="Horizontal" VerticalAlignment="Center">
                <TextBlock>Total No of Items:</TextBlock>
                <TextBox x:Name="totalItems" Width="180" HorizontalContentAlignment="Right"/>
            </StackPanel>
            <StackPanel Grid.Row="3" Orientation="Horizontal" VerticalAlignment="Center">
                <TextBlock>Total Amount</TextBlock>
                <TextBox x:Name="totalAmount" Width="178" HorizontalAlignment="Left" VerticalAlignment="Center" HorizontalContentAlignment="Right"/>
            </StackPanel>
            <StackPanel Grid.Row="7" Orientation="Vertical" VerticalAlignment="Center">
                <Button x:Name="calDisc" Content="Calc Discount" Width="100" Click="calDisc_Click"/>
                <Button x:Name="calTax" Content="Cal  Tax" Width="100" Margin="0,10,0,0" Click="calTax_Click"/>
            </StackPanel>
            <StackPanel Grid.Row="4" Orientation="Horizontal" VerticalAlignment="Center">
                <TextBlock>Discounted Amount:</TextBlock>
                <TextBlock x:Name="label"></TextBlock>
            </StackPanel>
            <StackPanel Grid.Row="5" Orientation="Horizontal" VerticalAlignment="Center">
                <TextBlock>Amount with Tax:</TextBlock>
                <TextBlock x:Name="labelA"></TextBlock>
            </StackPanel>
        </Grid>
    </Window>
    
    
    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
    {
        /// <summary>
        /// MainWindow.xaml 的交互逻辑
        /// </summary>
        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" ?>
    <configuration>
    	<configSections>
    		<section name="microsoft.scripting" type="Microsoft.Scripting.Hosting.Configuration.Section, Microsoft.Scripting"/>
    	</configSections>
    	<microsoft.scripting>
    		<languages>
    			<language names="IronPython;Python;py" extensions=".py" displayName="Python" type="IronPython.Runtime.PythonContext, IronPython"/>
    		</languages>
    	</microsoft.scripting>
    	<startup>
    		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    	</startup>
    </configuration>
    

    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<DateTime, string> GetTomo = today => today.AddDays(1).ToShortDateString();
                wroxDyn.GetTomorrow = GetTomo;
                Console.WriteLine("Tomorrow is {0}", wroxDyn.GetTomorrow(DateTime.Now));
            }
        }
        class WroxDynamicObject : DynamicObject
        {
            Dictionary<string, object> _dynamicData = new Dictionary<string, object>();
            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<DateTime, string> 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>();
                dynamic expObj=new ExpandoObject();
                ((IDictionary<string, object>)expObj).Add("john", 10);
                retList.Add(expObj);
                dynamic expObj1 = new ExpandoObject();
                ((IDictionary<string, object>)expObj1).Add("yang", DateTime.Now);
                retList.Add(expObj1);
                foreach(var dy in retList)
                {
                    var tempDic = (IDictionary<string, object>)dy;
                    foreach(var kv in tempDic)
                    {
                        Console.WriteLine(kv.Key);
                        Console.WriteLine(kv.Value);
                    }
                }
    
    
    
            }
        }
    
        
    
    }
    
    

    output

    john
    10
    yang
    2022/2/4 23:35:21
    
  • 相关阅读:
    模型层
    视图层,模板层
    ORM表关系建立
    CMakeList入门
    C++标准模板库
    C++基本语法
    g++应用说明
    Linux快捷键
    Git 操作备忘
    Block的详细介绍
  • 原文地址:https://www.cnblogs.com/johnyang/p/15863670.html
Copyright © 2020-2023  润新知