Devlooped.Dynamically 1.1.1

前缀已保留
dotnet add package Devlooped.Dynamically --version 1.1.1                
NuGet\Install-Package Devlooped.Dynamically -Version 1.1.1                
此命令旨在在 Visual Studio 的包管理器控制台中使用,因为它使用 NuGet 模块的 Install-Package 版本。
<PackageReference Include="Devlooped.Dynamically" Version="1.1.1" />                
对于支持 PackageReference 的项目,将此 XML 节点复制到项目文件中以引用包。
paket add Devlooped.Dynamically --version 1.1.1                
#r "nuget: Devlooped.Dynamically, 1.1.1"                
#r 指令可以用于 F# Interactive 和 Polyauss 语笔记本。将其复制到交互式工具或脚本源代码中,以引用包。
// 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);

您还可以通过在记录中提供可访问的静态 CreateCreateMany 工厂方法来可选地自定义特定记录的映射,因此,您可以通过在特定情况下按需提供它们来选择性地自定义映射。例如

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 语句,根据指定的泛型参数分配到正确的工厂。

此外,如果记录是部分记录,您还可以在记录类型本身上获得静态的 CreateCreateMany 方法,以提供更多便利,例如:

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,它非常灵活并且具有卓越的 性能特性。这个软件包确实提供了一个非常快的高速内存对象映射,比任何形式的序列化都要快得多且成本更低。

如前所述,提供的工厂不提供向后兼容性:如果您向记录添加属性或构造函数参数,该工厂在没有这些参数的有效负载中将继续失败。

赞助商

Clarius Org Kirill Osenkov MFB Technologies, Inc. Stephen Shaw Torutek DRIVE.NET, Inc. Daniel Gnägi Ashley Medway Keith Pickford Thomas Bolon Kori Francis Sean Killeen Toni Wenzel Giorgi Dalakishvili Mike James Dan Siegel Reuben Swartz Jacob Foshee alternate text is missing from this package README image Eric Johnson Norman Mackay Certify The Web Ix Technologies B.V. David JENNI Jonathan Oleg Kyrylchuk Charley Wu Jakob Tikjøb Andersen Seann Alexander Tino Hager Mark Seemann Angelo Belchior Ken Bonny Simon Cropp agileworks-eu sorahex Zheyu Shen Vezel

Sponsor this project  

了解更多关于 GitHub Sponsors 信息

此软件包中没有支持的框架资产。

了解更多关于 目标框架.NET Standard 信息。

NuGet 包

此软件包没有被任何 NuGet 包使用。

GitHub 仓库

此软件包没有被任何流行的 GitHub 仓库使用。

版本 下载 最后更新
1.1.1 155 2/14/2024
1.1.0 291 8/11/2023
1.0.2 313 11/18/2022
1.0.1 509 11/16/2022
1.0.0 309 11/14/2022
1.0.0-alpha 212 11/14/2022