在C#中⽣成代码的四种⽅式——包括.NET5中的
SourceGenerators
Microsoft在最新的C#版本中引⼊了Source Generator。这是⼀项新功能,可以让我们在代码编译时⽣成源代码。在本⽂中,我将介绍四种C#中的代码⽣成⽅式,以简化我们的⽇常⼯作。然后,您可以视情况选择正确的⽅法。
在 .NET 中,我们有以下⼏种⽅法来帮助我们⽣成代码:
Code snippets.
Reflection.
T4 Template.
[New] Source Generators in .NET 5.
Code snippets
Code snippets 是可重⽤的代码块,可以使⽤热键组合将其插⼊我们的代码⽂件中。例如,如果在Visual
Studio中键⼊prop然后
按Tab,VS将在您的类中⾃动⽣成⼀个属性,然后您可以轻松地替换属性名称。VS已经为我们提供了⼤量的内置的代码⽚段,
如prop,if,while,for,try,您可以在这⾥到所有的默认代码⽚段列表:C# Code Snippets[1]。
Code snippets 的好处是您可以替换参数。例如,当我们将MVVM模式⽤于UWP / Xamarin / WPF应⽤程序时,经常需要在实现INotifyPropertyChanged[2]接⼝的类中创建属性。如果您使⽤MvvmCross框架,它可能看起来像这样:
private ObservableCollection<Comment> _commentList;
public ObservableCollection<Comment> CommentList
{
get => _commentList;
set => SetProperty(ref _commentList, value);
}
我们不想复制/粘贴然后更改变量名,所以我创建了⼀个 Code snippet 来简化⼯作。创建⼀个名为myMvvm.snippet的新⽂件,然后复制并粘贴以下代码:
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="schemas.microsoft/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<Title>MvvmCross property</Title>
<Author>Xiaodi Yan</Author>
<Shortcut>mvxprop</Shortcut>
<Description>
A property in a ViewModel in the Xamarin project with MvvmCross.
</Description>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>Property</ID>
<ToolTip>Property name</ToolTip>
<Default>Property</Default>
</Literal>
<Object>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>string</Default>
</Object>
<Literal>
<ID>pProperty</ID>
<ToolTip>Private property name</ToolTip>
<Default>property</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[#region $Property$;
private $type$ _$pProperty$;
public $type$ $Property$
{
get => _$pProperty$;
set => SetProperty(ref _$pProperty$, value);
}
#endregion]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
在此 Code snippet 中,我们使⽤<Shortcut>指定快捷⽅式mvxprop,并使⽤<Declarations>声明⼀些参数。例如,我们声明了⼀个名为的参数Property,然后使⽤$Property将其插⼊到代码段中。您可以通过VS Tools 菜单中的Code Snippets Manager导⼊此 Code
snippet(或按Ctrl + K,Ctrl + B)。
现在,您可以键⼊mvxprop并按Tab,VS可以为您创建属性-您只需⼿动替换属性名称即可。
更多信息请参考:
Walkthrough: Create a code snippet[3]
Code snippet functions[4]
How to: Distribute code snippets[5]
Code snippets 适合重复使⽤以插⼊整个类或⽅法或属性。您还可以将 Code snippets 分发给其他⽤户。当我们创建新⽂件或 Class 或Method 时,这很有⽤。但是,如果要在完成后更新⽣成的代码,则必须删除现有代码,然后重新创建它。基本上,它可以节省⽆聊的复
制/粘贴时间,但仅此⽽已。
Reflection
Reflection(反射)⼴泛⽤于许多.NET框架和库中,例如ASP.NET Core[6],Entity Framework Core[7]等。它可以提供类型的[8]对象,该对象描述程序集,模块和类型,以便您可以动态创建类型的实例,从现有对象获取类型,然后调⽤其⽅法或访问其字段和属性。
当我们构建.NET应⽤程序时,它将⽣成程序集-例如.dll⽂件。这些程序集包含我们的模块,其中包含某些类型。类型包含成员。Reflection 能够获取这些信息。因此,我们可以动态加载新的.dll⽂件并调⽤它们的⽅法或事件,⽽⽆需编辑代码。动态表⽰它可以在运⾏时运⾏。换句话说,当我们编译应⽤程序时,.NET应⽤程序直到运⾏时才知道我们需要使⽤什么类型。通过这种⽅式,我们可以创建⼀个客户端,该客户端可以根据我们的规则动态执⾏其他程序集中的⽅法。如果我们遵循该规则更新其他程序集中的类,则不需要更新客户端代码。
让我们查看以下⽰例。您可以在我的⽰例项⽬中到它。我们在CodeGeneratorDemo.ReflectionDemo.Core项⽬中有⼀个ISpeaker接⼝,如下所⽰:
namespace CodeGeneratorDemo.ReflectionDemo.Core
{
public interface ISpeaker
{
string SayHello();
}
}
创建两个实现类:
ChineseSpeaker:
namespace CodeGeneratorDemo.ReflectionDemo.Core
visual studio和vs code的区别
{
public class ChineseSpeaker : ISpeaker
{
public string Name => this.GetType().ToString();
public string SayHello()
{
return "Nihao";
}
}
}
以及 EnglishSpeaker:
namespace CodeGeneratorDemo.ReflectionDemo.Core
{
public class EnglishSpeaker : ISpeaker
{
public string Name => this.GetType().ToString();
public string SayHello()
{
return "Hello!";
}
}
}
现在,我们可以使⽤ Reflection 来查ISpeaker接⼝的所有实现,并调⽤其⽅法或属性。
在CodeGeneratorDemo.ReflectionDemo项⽬中创建⼀个名为ReflectionHelper的新⽂件:
using CodeGeneratorDemo.ReflectionDemo.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace CodeGeneratorDemo.ReflectionDemo
{
public class ReflectionHelper
{
public static List<Type> GetAvailableSpeakers()
{
// You can also use AppDomain.CurrentDomain.GetAssemblies() to load all assemblies in the current domain.
// Get the specified assembly.
var assembly =
Assembly.LoadFrom(Path.Combine(Directory.GetCurrentDirectory(), "CodeGeneratorDemo.ReflectionDemo.Core.dll"));
// Find all the types in the assembly.
var types = assembly.GetTypes();
// Apply the filter to find the implementations of ISayHello interface.
var result = types.Where(x => x.IsClass && typeof(ISpeaker).IsAssignableFrom(x)).ToList();
// Or you can use types.Where(x => x.IsClass && x.GetInterfaces().Contains(typeof(ISpeaker))).ToList();
return result;
}
}
}
在此类中,我们加载包含所需类型的指定dll⽂件。然后,我们可以使⽤ Reflection 并应⽤LINQ查询来到所有ISpeaker接⼝的实现。在CodeGeneratorDemo.Client项⽬中,我们可以输出每个Speaker的Name属性和调⽤SayHello⽅法:
private static void ReflectionSample()
{
Console.WriteLine("Here is the Reflection sample:");
// Find all the speakers in the current domain
var availableSpeakers = ReflectionHelper.GetAvailableSpeakers();
foreach (var availableSpeaker in availableSpeakers)
{
// Create the instance of the type
var speaker = Activator.CreateInstance(availableSpeaker);
// Get the property info of the given property name
PropertyInfo namePropertyInfo = availableSpeaker.GetProperty("Name");
// Then you can get the value of the property
var name = namePropertyInfo?.GetValue(speaker)?.ToString();
Console.WriteLine($"I am {name}");
// Invoke the method of the instance
Console.WriteLine(availableSpeaker.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, speaker, null));
}
Console.WriteLine();
}
运⾏该程序,您将看到以下输出:
Here is the Reflection sample:
I am CodeGeneratorDemo.ReflectionDemo.Core.ChineseSpeaker
Nihao
I am CodeGeneratorDemo.ReflectionDemo.Core.EnglishSpeaker
Hello!
如果我们需要添加其他语⾔的其他Speaker,只需在同⼀项⽬中添加实现类。.NET Reflection 可以⾃动出所有必需的类并正确调⽤⽅法。
当我们创建插件类型的应⽤程序时,它⾮常有⽤。⾸先,我们创建接⼝并通过反射从客户端调⽤⽅法。然后,我们可以在客户端界⾯之后创建插件,这些插件可以作为* .dll⽂件动态加载并执⾏。
另⼀种情况是框架开发。作为框架开发⼈员,您将⽆法知道⽤户将创建哪些实现,因此只能使⽤ Reflection 来创建这些实例。例如在某些MVVM框架中,如果按照约定创建类,如xxxViewModel,该框架可以到所有 ViewModel 并使⽤ Reflection ⾃动加载它们。
通常,当⼈们谈论反射时,主要关注的是性能。因为它在运⾏时运⾏,所以从理论上讲,它⽐普通应⽤程序要慢⼀点。但是它在许多情况下都⾮常灵活,尤其是在开发框架的情况下。如果可以接受程序花费⼏秒钟(或仅⼏百毫秒)来加载程序集,则使⽤Reflection是没有问题的。
使⽤Reflection的所需的主要名称空间是System.Reflection[9]和System.Type[10]。您可能还需要了解以下术语:Assembly[11]
Module[12]
ConstructorInfo[13]
MethodInfo[14]
FieldInfo[15]
EventInfo[16]
PropertyInfo[17]
ParameterInfo[18]
CustomAttributeData[19]
更多信息请参考以下⽂档:
Reflection in .NET[20]
Viewing Type Information[21]
Dynamically Loading and Using Types[22]
T4 Template
T4 Text Template是⽂本块和可以⽣成⽂本⽂件的控制逻辑的混合体。T4表⽰text template transformation。您可以使⽤它在Visual Studio 中为 C# 和 Visual Basic ⽣成⽂件。但是⽣成的⽂件本⾝可以是任何类型的⽂本,例如* .txt⽂件,HTML⽂件或任何语⾔的程序源代码。您可以使⽤C#代码(或VB)来控制模板中的逻辑。⼏年前,我曾经使⽤NuGet包(EntityFramework Reverse POCO Generator)为EntityFramework⽣成POCO模型。它由T4 Template 实现。我只需要更新T4 Template 中的数据库连接字符串并保存它,然后T4 Template 就可以读取数据库信息并⾃动创建所有模型和⽅法。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。