将JavaScript对象转换为C#结构化类型并寻求优化实现方案
优化JS对象转C#层级结构的实现方案
需求背景
现有如下JavaScript对象:
const information = { inventory: { backpack: { cords: [12, 20, 30, 50], size: [8, 30] }, rood: { cords: [12, 20, 30, 50], size: [8, 30] }, primary: { cords: [12, 20, 30, 50], size: [8, 30] } }, game: { heal: { cords: [12, 20, 30, 50], size: [8, 30] }, cooldown: { cords: [12, 20, 30, 50], size: [8, 30] } }, menu: { exit: { cords: [12, 20, 30, 50], size: [8, 30] }, pause: { cords: [12, 20, 30, 50], size: [8, 30] } } };
需要转换为C#结构,实现类似JS中var b_cords = information.inventory.backpack.cords;的层级属性访问方式,同时要求:
cords对应System.Drawing.Rectangle类型(而非数组)size对应System.Drawing.Size类型(而非数组)
当前已实现嵌套类方案,但存在代码冗余、结构不够规整的问题:
public class information { internal class inventory { internal class backpack { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } internal class rood { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } internal class primary { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } } internal class game { internal class heal { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } internal class cooldown { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } } internal class menu { internal class exit { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } internal class pause { internal static Rectangle cords = Rectangle.Empty; internal static Size size = Size.Empty; } } }
优化方案
方案1:提取重复结构为基础类,减少冗余
先定义基础类封装通用的cords和size属性,再通过嵌套类组合层级结构,同时使用实例属性替代静态属性(更符合C#面向对象设计规范):
using System.Drawing; // 基础类:复用通用属性 public class UiElement { public Rectangle Cords { get; set; } = Rectangle.Empty; public Size Size { get; set; } = Size.Empty; } // 层级结构类 public class Information { public InventorySection Inventory { get; set; } = new InventorySection(); public GameSection Game { get; set; } = new GameSection(); public MenuSection Menu { get; set; } = new MenuSection(); public class InventorySection { public UiElement Backpack { get; set; } = new UiElement(); public UiElement Rood { get; set; } = new UiElement(); public UiElement Primary { get; set; } = new UiElement(); } public class GameSection { public UiElement Heal { get; set; } = new UiElement(); public UiElement Cooldown { get; set; } = new UiElement(); } public class MenuSection { public UiElement Exit { get; set; } = new UiElement(); public UiElement Pause { get; set; } = new UiElement(); } }
使用方式与JS完全一致:
var info = new Information(); Rectangle bCords = info.Inventory.Backpack.Cords;
方案2:结合JSON反序列化+自定义转换器(支持数组转Rectangle/Size)
如果需要从JSON配置文件加载数据,可通过自定义JSON转换器实现数组到Rectangle/Size的自动转换,兼顾灵活性与类型安全性:
步骤1:定义层级结构类
直接复用方案1的Information及相关嵌套类即可。
步骤2:实现自定义JSON转换器(以System.Text.Json为例)
using System.Drawing; using System.Text.Json; using System.Text.Json.Serialization; // 将[X,Y,Width,Height]数组转为Rectangle public class RectangleConverter : JsonConverter<Rectangle> { public override Rectangle Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("Rectangle需以数组格式传入"); var values = new int[4]; int index = 0; while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (index >= 4) throw new JsonException("Rectangle数组必须包含4个元素"); values[index++] = reader.GetInt32(); } return new Rectangle(values[0], values[1], values[2], values[3]); } public override void Write(Utf8JsonWriter writer, Rectangle value, JsonSerializerOptions options) { writer.WriteStartArray(); writer.WriteNumberValue(value.X); writer.WriteNumberValue(value.Y); writer.WriteNumberValue(value.Width); writer.WriteNumberValue(value.Height); writer.WriteEndArray(); } } // 将[Width,Height]数组转为Size public class SizeConverter : JsonConverter<Size> { public override Size Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("Size需以数组格式传入"); var values = new int[2]; int index = 0; while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (index >= 2) throw new JsonException("Size数组必须包含2个元素"); values[index++] = reader.GetInt32(); } return new Size(values[0], values[1]); } public override void Write(Utf8JsonWriter writer, Size value, JsonSerializerOptions options) { writer.WriteStartArray(); writer.WriteNumberValue(value.Width); writer.WriteNumberValue(value.Height); writer.WriteEndArray(); } }
步骤3:反序列化JSON到C#对象
// JS对象对应的JSON字符串 string json = @" { ""inventory"": { ""backpack"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] }, ""rood"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] }, ""primary"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] } }, ""game"": { ""heal"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] }, ""cooldown"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] } }, ""menu"": { ""exit"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] }, ""pause"": { ""cords"": [12, 20, 30, 50], ""size"": [8, 30] } } }"; // 配置转换器 var options = new JsonSerializerOptions(); options.Converters.Add(new RectangleConverter()); options.Converters.Add(new SizeConverter()); // 反序列化 Information info = JsonSerializer.Deserialize<Information>(json, options); // 访问属性 Rectangle bCords = info.Inventory.Backpack.Cords;
方案3:使用C# 9+记录类型(简洁不可变结构)
如果不需要修改属性值,可使用记录类型实现更简洁的不可变层级结构:
using System.Drawing; public record UiElement(Rectangle Cords, Size Size); public record Information( InventorySection Inventory, GameSection Game, MenuSection Menu); public record InventorySection( UiElement Backpack, UiElement Rood, UiElement Primary); public record GameSection( UiElement Heal, UiElement Cooldown); public record MenuSection( UiElement Exit, UiElement Pause);
初始化及使用示例:
var info = new Information( new InventorySection( new UiElement(new Rectangle(12,20,30,50), new Size(8,30)), new UiElement(new Rectangle(12,20,30,50), new Size(8,30)), new UiElement(new Rectangle(12,20,30,50), new Size(8,30)) ), new GameSection( new UiElement(new Rectangle(12,20,30,50), new Size(8,30)), new UiElement(new Rectangle(12,20,30,50), new Size(8,30)) ), new MenuSection( new UiElement(new Rectangle(12,20,30,50), new Size(8,30)), new UiElement(new Rectangle(12,20,30,50), new Size(8,30)) ) ); Rectangle bCords = info.Inventory.Backpack.Cords;
方案对比
- 方案1:适合静态配置场景,代码简洁易维护,无外部依赖
- 方案2:适合需要从JSON加载动态数据的场景,灵活性高
- 方案3:适合不可变数据场景,语法极简,需C# 9及以上版本支持
内容的提问来源于stack exchange,提问作者tecace1756
相关产品推荐
相关产品推荐

