+ SunnyMapper: 轻量级的对象映射框架,可以映射值类型(包括Struct),和以值类型构成的List和数组。

This commit is contained in:
Sunny 2021-09-30 22:34:28 +08:00
parent 6e74477a84
commit 995a1d0621
47 changed files with 501 additions and 48 deletions

Binary file not shown.

Binary file not shown.

View File

@ -162,8 +162,9 @@ namespace Sunny.UI.Demo
this.uiSymbolButton25.StyleCustomMode = true; this.uiSymbolButton25.StyleCustomMode = true;
this.uiSymbolButton25.Symbol = 61453; this.uiSymbolButton25.Symbol = 61453;
this.uiSymbolButton25.TabIndex = 112; this.uiSymbolButton25.TabIndex = 112;
this.uiSymbolButton25.Text = "Link"; this.uiSymbolButton25.Text = "类库";
this.uiSymbolButton25.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.uiSymbolButton25.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.uiSymbolButton25.Click += new System.EventHandler(this.uiSymbolButton25_Click);
// //
// uiImageButton4 // uiImageButton4
// //
@ -426,7 +427,6 @@ namespace Sunny.UI.Demo
// uiSymbolButton18 // uiSymbolButton18
// //
this.uiSymbolButton18.Cursor = System.Windows.Forms.Cursors.Hand; this.uiSymbolButton18.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiSymbolButton18.Enabled = false;
this.uiSymbolButton18.Font = new System.Drawing.Font("微软雅黑", 12F); this.uiSymbolButton18.Font = new System.Drawing.Font("微软雅黑", 12F);
this.uiSymbolButton18.Location = new System.Drawing.Point(30, 395); this.uiSymbolButton18.Location = new System.Drawing.Point(30, 395);
this.uiSymbolButton18.MinimumSize = new System.Drawing.Size(1, 1); this.uiSymbolButton18.MinimumSize = new System.Drawing.Size(1, 1);

View File

@ -57,5 +57,10 @@ namespace Sunny.UI.Demo
{ {
Console.WriteLine(uiSwitch1.Active); Console.WriteLine(uiSwitch1.Active);
} }
private void uiSymbolButton25_Click(object sender, EventArgs e)
{
Frame.SelectPage(5000);
}
} }
} }

View File

@ -80,6 +80,8 @@ namespace Sunny.UI.Demo
//直接增加一个页面,不在左侧列表显示 //直接增加一个页面,不在左侧列表显示
AddPage(new FColorful()); AddPage(new FColorful());
AddPage(new FCommon());
//选中第一个节点 //选中第一个节点
Aside.SelectPage(1002); Aside.SelectPage(1002);
} }

View File

@ -361,6 +361,13 @@
<Compile Include="Forms\FColorful.Designer.cs"> <Compile Include="Forms\FColorful.Designer.cs">
<DependentUpon>FColorful.cs</DependentUpon> <DependentUpon>FColorful.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Units\FCommon.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Units\FCommon.Designer.cs">
<DependentUpon>FCommon.cs</DependentUpon>
</Compile>
<Compile Include="Units\UMapperDemo.cs" />
<EmbeddedResource Include="Charts\FBarChartEx.resx"> <EmbeddedResource Include="Charts\FBarChartEx.resx">
<DependentUpon>FBarChartEx.cs</DependentUpon> <DependentUpon>FBarChartEx.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@ -521,6 +528,9 @@
<EmbeddedResource Include="Forms\FColorful.resx"> <EmbeddedResource Include="Forms\FColorful.resx">
<DependentUpon>FColorful.cs</DependentUpon> <DependentUpon>FColorful.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Units\FCommon.resx">
<DependentUpon>FCommon.cs</DependentUpon>
</EmbeddedResource>
<None Include="..\.editorconfig"> <None Include="..\.editorconfig">
<Link>.editorconfig</Link> <Link>.editorconfig</Link>
</None> </None>

68
SunnyUI.Demo/Units/FCommon.Designer.cs generated Normal file
View File

@ -0,0 +1,68 @@

namespace Sunny.UI.Demo
{
partial class FCommon
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.uiButton1 = new Sunny.UI.UIButton();
this.SuspendLayout();
//
// uiButton1
//
this.uiButton1.Cursor = System.Windows.Forms.Cursors.Hand;
this.uiButton1.Font = new System.Drawing.Font("微软雅黑", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.uiButton1.Location = new System.Drawing.Point(30, 60);
this.uiButton1.MinimumSize = new System.Drawing.Size(1, 1);
this.uiButton1.Name = "uiButton1";
this.uiButton1.Size = new System.Drawing.Size(100, 35);
this.uiButton1.TabIndex = 0;
this.uiButton1.Text = "Mapper";
this.uiButton1.Click += new System.EventHandler(this.uiButton1_Click);
//
// FCommon
//
this.AllowShowTitle = true;
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 21F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.uiButton1);
this.Name = "FCommon";
this.Padding = new System.Windows.Forms.Padding(0, 35, 0, 0);
this.PageIndex = 5000;
this.ShowTitle = true;
this.Symbol = 62098;
this.Text = "类库";
this.ResumeLayout(false);
}
#endregion
private UIButton uiButton1;
}
}

View File

@ -0,0 +1,15 @@
namespace Sunny.UI.Demo
{
public partial class FCommon : UIPage
{
public FCommon()
{
InitializeComponent();
}
private void uiButton1_Click(object sender, System.EventArgs e)
{
MapperDemo.Demo();
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,98 @@
using Sunny.UI.Static;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Sunny.UI.Demo
{
public static class MapperDemo
{
public static void Demo()
{
MapperT1 t1 = new MapperT1();
t1.A = "Hello world.";
t1.B = 1024;
t1.C = 768;
t1.D = DateTime.Now;
t1.E = new Point(100, 200);
t1.F = new MapperT3("Struct String", '\n', new MapperT4("I am T4"));
t1.G = new Size(100, 200);
t1.H = new List<string>() { "AA", "BB" };
t1.I = new string[2] { "AA", "BB" };
MapperT2 t2 = new MapperT2();
t2.MapperFrom(t1);
t2.WriteConsole();
}
}
public class MapperT1
{
public string A { get; set; }
public int B { get; set; }
public double C { get; set; }
public DateTime D { get; set; }
public Point E { get; set; }
public MapperT3 F { get; set; }
public Size G { get; set; }
public List<string> H { get; set; }
public string[] I { get; set; }
}
public class MapperT2
{
public string A { get; set; }
public int B { get; set; }
public double C { get; set; }
public DateTime D { get; set; }
public Point E { get; set; }
public MapperT3 F { get; set; }
public List<string> H { get; set; }
public string[] I { get; set; }
}
public struct MapperT3
{
public string String { get; set; }
public char Char { get; set; }
public MapperT4 T4 { get; set; }
public MapperT3(string str, char c, MapperT4 t4)
{
String = str;
Char = c;
T4 = t4;
}
}
public class MapperT4
{
public string String { get; set; }
public MapperT4()
{
}
public MapperT4(string str)
{
String = str;
}
}
}

181
SunnyUI/Common/UMapper.cs Normal file
View File

@ -0,0 +1,181 @@
/******************************************************************************
* SunnyUI
* CopyRight (C) 2012-2021 ShenYongHua().
* QQ群56829229 QQ17612584 EMailSunnyUI@QQ.Com
*
* Blog: https://www.cnblogs.com/yhuse
* Gitee: https://gitee.com/yhuse/SunnyUI
* GitHub: https://github.com/yhuse/SunnyUI
*
* SunnyUI.dll can be used for free under the GPL-3.0 license.
* If you use this code, please keep this note.
* 使
******************************************************************************
* : UMapper.cs
* : StructList和数组
* : V3.0
* : 2021-09-30
*
* 2021-09-30: V3.0.7
******************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Sunny.UI
{
public static class SunnyMapper
{
private static void Mapper<T1, T2>(T1 source, T2 dest)
where T1 : class
where T2 : class
{
if (source == null || dest == null)
{
return;
}
var listSource = source.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);
var listDest = dest.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);
foreach (var item in listDest)
{
if (listSource.NotContainsKey(item.Key)) continue;
var sourceInfo = listSource[item.Key];
Type sourceType = sourceInfo.PropertyType;
object sourceValue = sourceInfo.GetValue(source, null);
var destInfo = item.Value;
Type destType = item.Value.PropertyType;
if (!sourceType.Equals(destType)) continue;
if (sourceType.IsValueType)
{
//Console.WriteLine("ValueType: " + item.Key + ", " + sourceType.FullName);
destInfo.SetValue(dest, sourceValue, null);
}
else
{
if (sourceType == typeof(string))
{
//Console.WriteLine("String: " + item.Key + ", " + sourceType.FullName);
destInfo.SetValue(dest, sourceValue, null);
continue;
}
if (sourceType.IsList())
{
//Console.WriteLine("List: " + item.Key + ", " + sourceType.FullName);
Type[] sourceTypes = sourceType.GetGenericArguments();
Type[] destTypes = destType.GetGenericArguments();
if (sourceTypes.Length != 1) continue;
if (destTypes.Length != 1) continue;
if (!sourceTypes[0].Equals(destTypes[0])) continue;
if (sourceValue == null)
{
destInfo.SetValue(dest, null, null);
}
else
{
Type typeDataList = typeof(List<>).MakeGenericType(destTypes[0]);
MethodInfo AddInfo = typeDataList.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
if (AddInfo == null) continue;
if (sourceTypes[0].IsValueType || sourceTypes[0] == typeof(string))
{
object listvalue = typeDataList.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });
IEnumerable sourceList = sourceValue as IEnumerable;
foreach (var listItem in sourceList)
{
AddInfo.Invoke(listvalue, new[] { listItem });
}
destInfo.SetValue(dest, listvalue, null);
}
else
{
//暂时不考虑
}
}
continue;
}
if (sourceType.IsArray)
{
//Console.WriteLine("Array: " + item.Key + ", " + sourceType.FullName);
if (sourceValue == null)
{
destInfo.SetValue(dest, null, null);
}
else
{
ICollection sourceList = sourceValue as ICollection;
Type type = sourceType.GetElementType();
var array = Array.CreateInstance(type, sourceList.Count);
int index = 0;
foreach (var listItem in sourceList)
{
array.SetValue(listItem, index++);
}
destInfo.SetValue(dest, array, null);
}
continue;
}
if (sourceType.IsDictionary())
{
continue;
}
//类没有无参构造函数的话,创建有问题
//if (sourceType.IsClass)
//{
// if (sourceValue == null)
// {
// destInfo.SetValue(dest, null, null);
// }
// else
// {
// object obj = Activator.CreateInstance(sourceType, null);
// obj.MapperFrom(sourceValue);
// destInfo.SetValue(dest, obj, null);
// }
//
// continue;
//}
}
}
}
public static void MapperTo<T1, T2>(this T1 source, T2 dest)
where T1 : class
where T2 : class
{
Mapper(source, dest);
}
public static void MapperFrom<T1, T2>(this T1 dest, T2 source)
where T1 : class
where T2 : class
{
Mapper(source, dest);
}
public static Type GetArrayElementType(this Type t)
{
if (!t.IsArray) return null;
string name = t.FullName.Replace("[]", string.Empty);
return t.Assembly.GetType(name);
}
}
}

View File

@ -1,46 +0,0 @@
using System;
using System.Linq;
namespace Sunny.UI
{
public static class MapperEx
{
private static void Mapper<T>(T source, T dest)
{
var listSource = source.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);
var listDest = source.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);
foreach (var item in listDest)
{
if (listSource.ContainsKey(item.Key))
{
var sourceInfo = listSource[item.Key];
object sourceValue = sourceInfo.GetValue(source, null);
Type sourceType = sourceInfo.PropertyType;
Type destType = item.Value.PropertyType;
var destInfo = item.Value;
if (sourceType.IsValueType)
{
destInfo.SetValue(dest, sourceValue, null);
}
else
{
}
}
}
}
public static void MapperTo<T>(this T source, T dest)
{
Mapper(source, dest);
}
public static void MapperFrom<T>(this T dest, T source)
{
Mapper(source, dest);
}
}
}