将你的 C 代码编译到 .NET - 第 2 部分





5.00/5 (10投票s)
有了这个新的 OrangeC/C++ 编译器后端,你可以将你的 C 代码编译到 .NET。
关于 OrangeC 到 .NET 的文章
目录
引言
为了支持 .NET 方法、类、属性、枚举等,OrangeC 中实现了一种类似于 C# 的语言支持,例如
int a = 10;
printf("%s", a.ToString());
另一个来自 C++ 的特性,是启用了“using namespace
”的使用,例如
using namespace System;
using namespace System::IO;
那么,让我们看看如何使用 OrangeC 编译器的新特性来编译到 .NET。
使用 .NET 类型
.NET 类型不过是 .NET 类,因此,使用 OrangeC,你可以像在 C 中使用普通类型一样使用这些类和方法。为了更容易理解,让我们构建一个小的示例,使用 .NET string
类
int main()
{
System::String str("Hello World!!");
return 0;
}
通过这段代码,我们创建了类 String
的一个实例,为了方便起见,在编译器内部实现了一些关键字来处理 .NET 中最常见的类,即
对象
字符串
因此,要使用这些类型,使用原生关键字,我们有
__object
__string
让我们看一个简单的例子
int main()
{
__string str("Hello World!!");
__object obj = (__object)str;
printf("%s\n", str);
printf("%s\n", (__string)obj);
return 0;
}
让我们再看一个使用 .NET 类型的例子
int main()
{
System::DateTime dt(2017,10,10);
return 0;
}
托管和非托管数组
为了避免旧代码和新代码之间的兼容性问题,实现了一种语法变体来区分数组类型,因此,要创建一个托管数组,我们在变量名前使用 []
,要创建一个非托管数组,我们在变量名后使用 []
。为了更清楚地说明,让我们做一个小例子
int main()
{
int []a = { 0, 1, 2, 3, 4, 5 }; // Managed array
int b[] = { 0, 1, 2, 3, 4, 5 }; // Unmanaged array
return 0;
}
你可能在想,托管数组和非托管数组到底有什么区别?
所以,为了回答这个问题,让我们看看为每种数组生成的代码(我使用了一个反编译器来获取这段代码)
public static unsafe int main(int argc, void* argv)
{
// The managed array
int[] a = new int[] { 0, 1, 2, 3, 4, 5 };
// The unmanaged array
int32[24] b = null;
*(&b + 4) = 1;
*(&b + 8) = 2;
*(&b + 12) = 3;
*(&b + 16) = 4;
*(&b + 20) = 5;
return 0;
}
数组的导航仍然相同
int main()
{
int []a = { 0, 1, 2, 3, 4, 5 }; // Managed array
int b[] = { 0, 1, 2, 3, 4, 5 }; // Unmanaged array
printf("Printing all elements of managed array: ");
for(int i=0;i<a.Length;i++) // Look at the usage of property "Length"
{
if(i != 0)
printf(", ");
printf("%d", a[i]);
}
printf("\n");
printf("Printing all elements of unmanaged array: ");
for(int i=0;i<sizeof(b)/sizeof(int);i++)
{
if(i != 0)
printf(", ");
printf("%d", a[i]);
}
printf("\n");
return 0;
}
使用 .NET 方法
使用 .NET 方法的方式与 C# 类似
<实例>.<方法>([参数]);
不同之处在于 enum
和 static
类
[命名空间]::<静态类>::<方法 | 属性 | 枚举>[ ( [参数] ) ];
让我们看一个单一的例子,展示这些情况
#include <stdio.h>
#include <stdlib.h>
using namespace System;
using namespace System::Runtime;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
int main()
{
AppDomain ad = AppDomain::CurrentDomain;
AssemblyName am("TestAsm");
AssemblyBuilder ab = ad.DefineDynamicAssembly(am, AssemblyBuilderAccess::Save);
ModuleBuilder mb = ab.DefineDynamicModule("testmod", "TestAsm.exe");
TypeBuilder tb = mb.DefineType("mytype", TypeAttributes::Public);
MethodBuilder metb = tb.DefineMethod("hi", MethodAttributes::Public |
MethodAttributes::Static, NULL, NULL);
ab.SetEntryPoint(metb);
ILGenerator il = metb.GetILGenerator();
il.EmitWriteLine("Hello World");
il.Emit(OpCodes::Ret);
tb.CreateType();
ab.Save("TestAsm.exe");
Console::WriteLine("TestAsm.exe created!");
return 0;
}
让我们构建另一个例子,实现一个接收 .NET 类型作为参数并使用一些复杂方法调用的函数
#include <stdio.h>
void test(System::DateTime dt, System::String &str)
{
str = dt.ToString("dd/MM/yyyy");
}
int main()
{
__string str;
System::DateTime dt1 = System::DateTime::Today;
System::DateTime dt2 = dt1.AddDays(30);
str = dt1.ToString("dd,MM,yyyy") + "/" + dt2.ToString("dd.MM.yy");
__string part1 = str.Substring(0, str.IndexOf("/"));
__string part2 = str.Substring(str.IndexOf("/") + 1);
str = str.Replace(",", "").Replace("/", "").Replace("10", "Hi!");
printf("Part1 = %s\nPart2 = %s\nNew string = %s\n", part1, part2, str);
test(dt1, str);
printf("%s\n", str);
return 0;
}
最后,我们将构建一个混合托管和非托管代码,为此,我们将构建一个链表,其中链表的 struct
持有托管类型
#include <stdio.h>
#include <stdlib.h>
int getAge(System::DateTime reference, System::DateTime birthday);
typedef struct node
{
__string name;
System::DateTime bornDate;
struct node* next;
} node_t;
void push(node_t* head, __string name, System::DateTime born)
{
node_t* current = head;
while (current->next != NULL)
current = current->next;
current->next = (node_t*)malloc(sizeof(node_t));
current->next->name = name;
current->next->bornDate = born;
current->next->next = NULL;
}
void print_list(node_t * head) {
node_t * current = head;
System::DateTime today = System::DateTime::Today;
while (current != NULL) {
System::Console::WriteLine(((__string)"").PadLeft(80, System::Convert::ToChar("-")));
System::Console::WriteLine("Name: {0}\nBorn date: {1}\nAge: {2}\n",
current->name, current->bornDate.ToString("dd/MM/yyyy"), getAge(today, current->bornDate));
current = current->next;
}
}
int getAge(System::DateTime reference, System::DateTime birthday)
{
int age = reference.Year - birthday.Year;
if(System::DateTime::op_LessThan(reference, birthday.AddYears(age)))
age--;
return age;
}
int main()
{
node_t* head = (node_t*)malloc(sizeof(node_t));
head->name = "Alexandre";
head->bornDate = System::DateTime::Today;
head->next = NULL;
System::DateTime today = System::DateTime::Today;
for(int i=0;i<15;i++)
push(head, "Teste", today.AddDays(i + 1).AddYears(-24));
print_list(head);
return 0;
}
使用 Try / Catch / Finally
异常系统极大地帮助开发人员处理错误,因此 C 代码变得与 .NET 错误系统和异常系统更兼容,我们实现了对 try
/ catch
/ finally
的支持,这些命令的使用就像在 C# 中一样,让我们看看
using namespace System;
using namespace System::IO;
int main()
{
__try
{
File.Open("some file that doesn't exist", FileMode::Open);
}
__catch (Exception e)
{
Console::WriteLine(e.Message);
}
__finally
{
printf("Finally!");
}
}
历史
- 2017年10月12日:创建文章