Devlooped.Dynamically 1.1.1
前缀已保留
dotnet add package Devlooped.Dynamically --version 1.1.1
NuGet\Install-Package Devlooped.Dynamically -Version 1.1.1
<PackageReference Include="Devlooped.Dynamically" Version="1.1.1" />
paket add Devlooped.Dynamically --version 1.1.1
#r "nuget: Devlooped.Dynamically, 1.1.1"
// Install Devlooped.Dynamically as a Cake Addin #addin nuget:?package=Devlooped.Dynamically&version=1.1.1 // Install Devlooped.Dynamically as a Cake Tool #tool nuget:?package=Devlooped.Dynamically&version=1.1.1
使用编译时源生成器从动态数据中实例化与兼容的形状的记录类型,内存中无反射或序列化。
用法
为您的数据类型创建记录
public record Point(int X, int Y);
public record Line(Point Start, Point End);
public record Drawing(Line[] Lines);
此项目将生成一个 Dynamically
类,其中包含一个工厂方法,可以从具有兼容形状的具有相同结构的数据对象创建这些记录的实例,例如
var data = new
{
Lines = new[]
{
new
{
Start = new { X = 50, Y = 0 },
End = new { X = 0, Y = 100 },
},
new
{
Start = new { X = 50, Y = 0 },
End = new { X = 0, Y = 100 },
}
}
};
Drawing drawing = Dynamically.Create<Drawing>(data);
除了动态对象(或例如,ExpandoObject)外,您还可以传递来自具有相同结构的不同程序集的强类型对象。这允许快速内存中对象映射,无需任何序列化或额外分配。
此工厂适用于 Newtonsoft.Json 反序列化对象,例如
// elsewhere, you got an in-memory Json.NET object model, perhaps with:
dynamic data = JsonConvert.DeserializeObject(json);
// Subsequently, you can turn it into your strongly-typed records:
Drawing drawing = Dynamically.Create<Drawing>(data);
您还可以通过在记录中提供可访问的静态 Create
或 CreateMany
工厂方法来可选地自定义特定记录的映射,因此,您可以通过在特定情况下按需提供它们来选择性地自定义映射。例如
partial record Drawing
{
// Customize creation of a single Drawing from a dynamic value
public static Drawing Create(dynamic value);
// Customize creation of a list of Drawings from a dynamic value
public static List<Drawing> CreateMany(dynamic value);
}
如何工作
本软件包在编译时分析您的记录形状并创建一个工厂,该工厂能够从动态对象中创建实例。为此,它仅访问动态对象的属性并将它们传递给记录构造函数(或其属性)。这意味着数据必须具有至少预期的值才能使转换成功。
在编译时还会生成静态的 Dynamically.Create
泛型方法,它包含一个 switch 语句,根据指定的泛型参数分配到正确的工厂。
此外,如果记录是部分记录,您还可以在记录类型本身上获得静态的 Create
和 CreateMany
方法,以提供更多便利,例如:
partial record Drawing
{
public static Drawing Create(dynamic value);
public static List<Drawing> CreateMany(dynamic value);
}
注意:这些方法仅在您的记录尚未包含它们的情况下提供。
示例
对于上述的 Drawing/Line/Point
记录示例,您将获得一个生成的 Dynamically
类型,如下所示:
static partial class Dynamically
{
public static partial T Create<T>(dynamic data)
{
return typeof(T) switch
{
Type t when t == typeof(Drawing) => (T)Drawing.Create(data),
_ => throw new NotSupportedException(),
};
}
}
如果 Drawing
记录是部分记录,则 Create
方法将像下面这样:
partial record Drawing
{
public static Drawing Create(dynamic value)
=> DrawingFactory.Create(value);
}
生成的 DrawingFactory
类将如下所示:
static partial class DrawingFactory
{
public static Drawing Create(dynamic value)
{
if (value is null)
throw new ArgumentNullException(nameof(value));
try
{
return new Drawing(Line.CreateMany(value.Lines));
}
catch (RuntimeBinderException e)
{
var valueAsm = ((object)value).GetType().Assembly.GetName();
var thisAsm = typeof(DrawingFactory).Assembly.GetName();
throw new ArgumentException(
$"Incompatible {nameof(Drawing)} value. Cannot convert value from '{valueAsm.Name}, Version={valueAsm.Version}' to '{thisAsm.Name}, Version={thisAsm.Version}'.",
nameof(value), e);
}
}
public static List<Drawing> CreateMany(dynamic value)
{
var result = new List<Drawing>();
foreach (var item in value)
{
result.Add(Create(item));
}
return result;
}
}
与前述 Line
工厂方法非常相似,它将实例化多个点,其中 Point
可能是最有趣的。
static partial class PointFactory
{
public static Point Create(dynamic value)
{
if (value is null)
throw new ArgumentNullException(nameof(value));
try
{
return new Point((global::System.Int32)value.X, (global::System.Int32)value.Y);
}
catch (RuntimeBinderException e)
{
var valueAsm = ((object)value).GetType().Assembly.GetName();
var thisAsm = typeof(PointFactory).Assembly.GetName();
throw new ArgumentException(
$"Incompatible {nameof(Point)} value. Cannot convert value from '{valueAsm.Name}, Version={valueAsm.Version}' to '{thisAsm.Name}, Version={thisAsm.Version}'.",
nameof(value), e);
}
}
public static List<Point> CreateMany(dynamic value)
{
var result = new List<Point>();
foreach (var item in value)
{
result.Add(Create(item));
}
return result;
}
}
如您所见,工厂方法非常简单直接,并且具有优异的运行时性能特点,因为没有使用反射,内置的 C# 动态基础结构负责处理繁重的工作。生成的代码基本上是您手动编写的用于转换整个对象层次结构的代码。
限制
本软件包并不意味着是一个完整的对象映射器。例如,您可以使用 AutoMapper,它非常灵活并且具有卓越的 性能特性。这个软件包确实提供了一个非常快的高速内存对象映射,比任何形式的序列化都要快得多且成本更低。
如前所述,提供的工厂不提供向后兼容性:如果您向记录添加属性或构造函数参数,该工厂在没有这些参数的有效负载中将继续失败。
赞助商
-
.NETStandard 2.0
- Microsoft.CSharp (≥ 4.7.0)
NuGet 包
此软件包没有被任何 NuGet 包使用。
GitHub 仓库
此软件包没有被任何流行的 GitHub 仓库使用。