目录
介绍
C#中的动态语言运行时DLR
DynamicObject和ExpandoObject
ExpandoObject
DynamicObject
在.NET中使用IronPython
结论
尽管C#属于静态类型语言,但在该语言的最新版本中添加了一些动态功能。在本文中,我想展示动态语言运行时(DLR)如何在C#、DynamicObject和ExpandoObject中工作,以及IronPython在.NET中的最简单用法。
DLR(动态语言运行时)自.NET 4.0开始添加,代表IronPython和IronRuby等动态语言的运行时环境。
为了理解这个创新的本质,你需要知道静态类型和动态类型的语言之间的区别。在具有静态类型的语言中,所有类型及其成员——属性和方法的识别发生在编译阶段,而在动态语言中,系统在执行之前对类型的属性和方法一无所知。
由于这个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多次更改其类型,此代码也可以正常工作。这是dynamic和var之间的关键区别。对于使用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行上,我们将看到一个错误,因为该+=操作不能应用于object和int类型。使用声明为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.00和Item.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 - 使用动态变量示例的结果
在这一部分中,我们通过示例检查了动态类型的使用。
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 { "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)(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与ExpandoObject非常相似。但是,在DynamicObject的情况下,我们需要通过继承DynamicObject并实现其方法来创建自己的类:
这些方法中的每一个都具有相同的检测模型:它们都返回一个布尔值,指示操作是否成功。作为第一个参数,它们都采用活页夹或活页夹对象。如果方法表示对索引器或可以接受参数的对象方法的调用,则object[]数组用作第二个参数——它存储传递给方法或索引器的参数。
几乎所有的操作,除了设置和删除属性和索引器外,都会返回一个特定的值(例如,如果我们获取一个属性的值。在这种情况下,使用第三个参数out对象值,其目的是存储返回的object。
让我们通过创建一个dynamic对象类来举个例子:
class Item_mast : DynamicObject
{
Dictionary members = new Dictionary();
// 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
使用该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 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 = 1和item.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属性的值将增加。
优点是您可以在使用动态对象时重新定义动态对象的行为,即,您实际上可以自己实现可动态扩展的对象。
在这一部分中,我们检查了DynamicObject和ExpandoObject示例的用法。
看起来为什么我们需要更多的语言,尤其是那些在另一种C#语言中使用的语言?但是,DLR环境的关键点之一是对IronPython和IronRuby等语言的支持。这在编写功能性客户端脚本时很有用。甚至可以说,现在客户端脚本的创建已经很普遍了,很多程序甚至游戏都支持添加用各种语言编写的客户端脚本。此外,可能有一些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