C# .NET中的动态语言运行时

目录

介绍

C#中的动态语言运行时DLR

DynamicObject和ExpandoObject

ExpandoObject

DynamicObject

在.NET中使用IronPython

结论


介绍

尽管C#属于静态类型语言,但在该语言的最新版本中添加了一些动态功能。在本文中,我想展示动态语言运行时(DLR)如何在C#DynamicObjectExpandoObject中工作,以及IronPython.NET中的最简单用法。

C#中的动态语言运行时DLR

DLR(动态语言运行时).NET 4.0开始添加,代表IronPythonIronRuby等动态语言的运行时环境。

为了理解这个创新的本质,你需要知道静态类型和动态类型的语言之间的区别。在具有静态类型的语言中,所有类型及其成员——属性和方法的识别发生在编译阶段,而在动态语言中,系统在执行之前对类型的属性和方法一无所知。

由于这个DLR环境,C#可以创建其成员在程序执行阶段被识别的动态对象,并将它们与具有静态类型的传统对象一起使用。

动态类型的使用是在C#中使用DLR的关键点,因此您可以在编译阶段跳过类型检查。此外,声明为动态的对象可以在程序运行期间更改其类型。例如:

class Program
{
    static void Main(string[] args)
    {
            dynamic x = 3;     // here x is a integer
            Console.WriteLine(x);
 
            x = "Hello world"; // now x is a string
            Console.WriteLine(x);
 
            x = new Item_mast() 
                { ItemId=1,ItemDesсription="Pen",Cost=10 }; // now x is a Item_mast
            Console.WriteLine(x);
            Console.ReadLine();        
    }
}
  public class Item_mast
        {
            public int ItemId { get; set; }
            public string ItemDesсription { get; set; }
            public int Cost { get; set; }
 
            public override string ToString()
            {
                return ItemId.ToString() + ", "+ ItemDesсription + " " + Cost.ToString();
            }
        }

结果如图1所示:

1 - 基本DLR示例

让我们稍微描述一下代码。即使变量x多次更改其类型,此代码也可以正常工作。这是dynamicvar之间的关键区别。对于使用var关键字声明的变量,类型在编译时输出,然后在运行时不会更改。此外,您可以注意到dynamic类型和object类型之间的一些相似之处。我们可以很容易地替换表达式:

dynamic x = 3;

至:

object x = 3

我们有同样的结果。

但是,object类型也存在差异。例如:

object obj = 24;
dynamic dyn = 24;
obj += 4; // we can not do it!!!
dyn += 4; // now is ok

obj += 4行上,我们将看到一个错误,因为该+=操作不能应用于objectint类型。使用声明为dynamic的变量,这是可能的,因为它的类型只会在运行时知道。
需要注意的是,dynamic不仅可以应用于变量,还可以应用于方法和属性。让我们在class中进行更改并考虑下一个示例:

public class Item_mast
        {
            public int ItemId { get; set; }
            public string ItemDesсription { get; set; }
            public dynamic Cost { get; set; } 
 
            public dynamic GetPrice(dynamic value, string format)
            {
                if (format == "string")
                {
                    return value + " dollar";
                }
                else if (format == "int")
                {
                    return value;
                }
                else
                {
                    return 0.0;
                }
            }

Item_mass class定义了一个动态Cost属性,因此在为该属性设置值时,我们可以同时写Item.Cost=10.00Item.Cost="ten"。这两个选项都是正确的。还有一种GetPrice方法可以返回动态值。例如,根据参数,我们可以返回string价格表示或数字表示。该方法还将动态作为参数。因此,我们可以同时传递整数和小数作为收入值。我们来看看具体的应用:

dynamic item1 = new Item_mast() { ItemId = 1, ItemDesсription = "Pen", Cost = 10 };
            Console.WriteLine(item1);
            Console.WriteLine(item1.GetPrice(10.00, "int"));
 
            dynamic item2 = new Item_mast() 
                            { ItemId = 2, ItemDesсription = "Pencil", Cost = "five" };
            Console.WriteLine(item2);
            Console.WriteLine(item2.GetPrice(5, "string"));
            Console.ReadLine();

结果,我们将拥有(图 2):

2 - 使用动态变量示例的结果

在这一部分中,我们通过示例检查了动态类型的使用。

DynamicObjectExpandoObject

ExpandoObject

C#/.NET开发能够创建非常类似于JavaScript中使用的动态对象。这种可能性是通过使用命名空间Dynamic,特别是ExpandoObject类来提供的。

让我们考虑一个例子:

dynamic viewbag = new System.Dynamic.ExpandoObject();
            viewbag.ItemId = 1;
            viewbag.ItemDesсription = "Pen";
            viewbag.Cost = 10;

            viewbag.Categories = new List<string> { "Flex", "Soft", "Luxury" };

            Console.WriteLine($"{viewbag.ItemId} ; 
                             {viewbag.ItemDesсription} ; {viewbag.Cost}");
            foreach (var cat in viewbag.Categories)
                Console.WriteLine(cat);

            //declare method
            viewbag.IncrementCost = (Action<int>)(x => viewbag.Cost += x);
            viewbag.IncrementCost(6); // Increase Cost 
            Console.WriteLine($"{viewbag.ItemId} ; 
                                {viewbag.ItemDesсription} ; {viewbag.Cost}");

            Console.ReadLine();

3 中的结果:

3 - 使用ExpandoObject()的示例

动态ExpandoObject对象可以声明任何可以表示各种对象的属性。您还可以使用委托设置方法。

DynamicObject

该类DynamicObjectExpandoObject非常相似。但是,在DynamicObject的情况下,我们需要通过继承DynamicObject并实现其方法来创建自己的类:

  • TryBinaryOperation():在两个对象之间执行二元运算。等价于标准的二元运算,例如x + y
  • TryConvert():执行到特定类型的转换。相当于C#中的基本转换,例如(SomeType)obj
  • TryCreateInstance(): 创建一个对象的实例
  • TryDeleteIndex():删除索引器
  • TryDeleteMember(): 删除一个属性或方法
  • TryGetIndex():通过索引器按索引获取元素。在C#中,它可以等价于以下表达式int x = collection[i]
  • TryGetMember(): 获取属性的价值。相当于访问一个属性,例如,string n = item1.ItemDescription
  • TryInvoke(): 调用一个对象作为委托
  • TryInvokeMember(): 方法调用
  • TrySetIndex():通过索引器按索引设置元素。在C#中,它可以等价于以下表达式collection[i] = x;
  • TrySetMember(): 设置属性。相当于将一个项目值分配给一个property.Itemdescription = "Pen"
  • TryUnaryOperation():执行类似于C#中的一元运算的一元运算:x++

这些方法中的每一个都具有相同的检测模型:它们都返回一个布尔值,指示操作是否成功。作为第一个参数,它们都采用活页夹或活页夹对象。如果方法表示对索引器或可以接受参数的对象方法的调用,则object[]数组用作第二个参数——它存储传递给方法或索引器的参数。

几乎所有的操作,除了设置和删除属性和索引器外,都会返回一个特定的值(例如,如果我们获取一个属性的值。在这种情况下,使用第三个参数out对象值,其目的是存储返回的object

让我们通过创建一个dynamic对象类来举个例子:

class Item_mast : DynamicObject
        {
            Dictionary<string, object> members = new Dictionary<string, object>();
 
            // set prop
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                members[binder.Name] = value;
                return true;
            }
            // get prop
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                result = null;
                if (members.ContainsKey(binder.Name))
                {
                    result = members[binder.Name];
                    return true;
                }
                return false;
            }
            // call method
            public override bool TryInvokeMember
            (InvokeMemberBinder binder, object[] args, out object result)
            {
                dynamic method = members[binder.Name];
                result = method((int)args[0]);
                return result != null;
            }
        }

我们不能直接从DynamicObject中创建对象,所以我们的Item_mast类是一个子类。在我们的类中,我们重新定义了三个方法。我们还使用Dictionary<string, object>成员来存储所有类成员,但我们也将它用于属性和方法。在这本字典中,这里的keys是属性和方法的names,而values是这些属性的values
使用该TrySetMember()方法,我们设置属性:

bool TrySetMember(SetMemberBinder binder, object value)

在这里,binder参数存储要设置的属性的名称(binder.Name),value是它需要设置的值。
TryGetMember是我们用来获取属性值的重写方法。

bool TryGetMember(GetMemberBinder binder, out object result)

同样,binder包含属性的名称,结果参数将包含结果的值。

The TryInvokeMember method is defined for calling methods:

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
    dynamic method = members[binder.Name];
    result = method((int)args[0]);
    return result != null;
}

首先,使用binder,我们获取方法,然后将args[0]参数传递给它,首先将其转换为int类型,然后在result参数中设置方法的结果。也就是说,在这种情况下,假设该方法将采用该int类型的一个参数并返回一些结果。让我们举一个在我们的应用程序中使用类的例子:
现在让我们在程序中应用这个类:

static void Main(string[] args)
{
   dynamic item = new Item_mast();
            item.ItemId = 1;
            item.ItemDesсription = "Pen";
            item.Cost = 10;
            Func<int, int> Incr = delegate (int x) { item.Cost += x; return item.Cost; };
            item.IncrementCost = Incr;
            Console.WriteLine($"{item.ItemId} ; {item.ItemDesсription} ; {item.Cost}");         
            item.IncrementCost(6);
            Console.WriteLine($"{item.ItemId} ; {item.ItemDesсription} ; {item.Cost}");
            Console.ReadLine();
}

表达式item.ItemId = 1item.ItemDescription = "Pen"将调用TrySetMember方法,数字将作为第一个变体中的第二个参数传递给该方法,而字符串"Pen"将在第二个变体中传递。
返回item.Cost调用TryGetMember方法。
此外,该item对象还定义了一个IncrementCost方法,该方法表示匿名委托delegate (int x) { item.Cost+=x; return item.Cost; }的操作。委托获取数字x,将Cost属性增加此数字并返回新值item.Cost。当调用此方法时,将访问该TryInvokeMember方法。因此,item.Cost属性的值将增加。

优点是您可以在使用动态对象时重新定义动态对象的行为,即,您实际上可以自己实现可动态扩展的对象。

在这一部分中,我们检查了DynamicObjectExpandoObject示例的用法。

.NET中使用IronPython

看起来为什么我们需要更多的语言,尤其是那些在另一种C#语言中使用的语言?但是,DLR环境的关键点之一是对IronPythonIronRuby等语言的支持。这在编写功能性客户端脚本时很有用。甚至可以说,现在客户端脚本的创建已经很普遍了,很多程序甚至游戏都支持添加用各种语言编写的客户端脚本。此外,可能有一些Python库的功能在.NET中可能不可用。在这种情况下,IronPython可以再次帮助我们。

让我们看一个例子。首先,我们需要添加所有必要的NuGet包。为此,我将使用批处理管理器。首先,让我们添加DLR包(图 4)。

4 - 添加DLR

接下来,添加IronPython(图 5):

5 - 添加 IronPython

让我们添加最简单的代码,并且我们已经使用了python

class Program
    {
        static void Main(string[] args)
        {
            ScriptEngine engine = Python.CreateEngine();
            engine.Execute("print 'hello, world'");
        }
    }

结果,我们有(图6):

6 - 使用IronPyton

这里使用了Python表达式, 它将print 'hello, world'输出string到控制台。要创建执行脚本的引擎,需要使用该ScriptEngine类。它的Execute()方法执行脚本。
此外,我们可以创建一个文件,例如helloword.py并将文件的内容直接粘贴到我们的代码中:

engine.ExecuteFile("D://helloword.py");

此外,该ScriptScope对象允许您通过接收或安装脚本与脚本进行交互。但是,这已经超出了本文的范围。

结论

总之,我们研究了动态语言运行时(DLR)C#中的工作方式、如何使用DynamicObject以及ExpandoObject 并且IronPython如何在.NET中最简单的示例中工作。

https://www.codeproject.com/Articles/5323867/Dynamic-Language-Runtime-in-Csharp-NET

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值