当前位置:网站首页>C knowledge
C knowledge
2022-04-23 20:45:00 【Wind god Shura envoy】
C# Use Emit A deep clone
Someone asked , How many methods are there to copy all the properties of one class to another class ? This is to ask how many methods deep cloning has , It's easy to think of three . Direct copy , Reflection replication , Serialization replication . But the faster ones are Expression tree copy
IL Copy
Two , This article mainly talks about the last
About expression tree replication , See Fast Deep Copy by Expression Trees (C#) - CodeProject
You need to know a little first IL
Of , It's easier to say later , Suppose you know IL
What is it? , It's easy to know IL
How to write , Then start developing functions . The first step is to name , Because you need to copy all the properties of one class to another class , Need to call method , And the method needs a name , So the first step is to name .
To create a method public void Clone<T>(T source, T los)
I used the following code
var dynamicMethod = new DynamicMethod("Clone", null, new[] {
typeof(T), typeof(T) });
The first parameter to create the method is easy to see , I won't explain , The second parameter is the return value of the method , Because the return is void
So you don't have to write . The third parameter is the parameter of the function , Just use the type , If there are multiple parameters, it is to write an array , If you find something here that you don't understand , Please tell me .
But after defining the method, you need to write the code inside the method , You need to use ILGenerator
, Use his Emit
Method , This method is very fast , You need to know when using IL Of , If you don't know , No problem , I'll be careful next .
ILGenerator generator = dynamicMethod.GetILGenerator();
You need to get all the properties of the type , Although reflection is used here , But only once , Because the reflection method here is to write IL Code , It can be used many times after writing , Maybe the speed is not fast for the first time , But then the speed is about the same as that of writing and compiling your own code , So it is recommended to use this method . You can use it yourself dot trace Go check the performance , What I see is that the performance is very good .
Take out the code that all attributes can read and write foreach (var temp in typeof(T).GetProperties().Where(temp=>temp.CanRead&&temp.CanWrite))
see IL
You need to put the first parameter on the left first , The second parameter is placed on the right , Call the... Of the second parameter get
Set the value of the first parameter set
The normal code of the corresponding attribute looks like
los.foo=source.foo;
there foo
Just get an attribute , It's random , Write out the IL Please see the following .
Ldarg_1 //los
Ldarg_0 //s
callvirt instance string lindexi.Foo::get_Name()
callvirt instance void lindexi.Foo::set_Name(string)
ret
From the code above callvirt
Use a method , Corresponding press in parameters , So you can get the method by reflection , Then call this method , So write the code, see below
generator.Emit(OpCodes.Ldarg_1);// los
generator.Emit(OpCodes.Ldarg_0);// s
generator.Emit(OpCodes.Callvirt,temp.GetMethod);
generator.Emit(OpCodes.Callvirt, temp.SetMethod);
Because you can take this out of the transformation method , So here's all the code
private static void CloneObjectWithIL<T>(T source, T los)
{
var dynamicMethod = new DynamicMethod("Clone", null, new[] {
typeof(T), typeof(T) });
ILGenerator generator = dynamicMethod.GetILGenerator();
foreach (var temp in typeof(T).GetProperties().Where(temp=>temp.CanRead&&temp.CanWrite))
{
generator.Emit(OpCodes.Ldarg_1);// los
generator.Emit(OpCodes.Ldarg_0);// s
generator.Emit(OpCodes.Callvirt,temp.GetMethod);
generator.Emit(OpCodes.Callvirt, temp.SetMethod);
}
generator.Emit(OpCodes.Ret);
var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
clone(source, los);
}
If you test this method , Then you will find , This method will appear for classes that are invisible to this method MethodAccessException
, So the incoming class needs this method and can directly use .
//A.dll
public class Foo
{
}
CloneObjectWithIL(foo1,foo2);
//B.dll
private static void CloneObjectWithIL<T>(T source, T los)
At this time... Cannot be used
outside , For static properties , Using the above code will also make an error , Because the access of static properties does not have permission , So look at the revised .
/// <summary>
/// Provides fast object deep replication
/// </summary>
public static class Clone
{
/// <summary>
/// Provide use IL Fast object deep copy
/// This method is required to have T Accessible
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"> Source </param>
/// <param name="los"> Copy properties from source </param>
/// <exception cref="MethodAccessException"> If you enter T There is no way to access , Then this exception will appear </exception>
// ReSharper disable once InconsistentNaming
public static void CloneObjectWithIL<T>(T source, T los)
{
// See http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
if (CachedIl.ContainsKey(typeof(T)))
{
((Action<T, T>) CachedIl[typeof(T)])(source, los);
return;
}
var dynamicMethod = new DynamicMethod("Clone", null, new[] {
typeof(T), typeof(T) });
ILGenerator generator = dynamicMethod.GetILGenerator();
foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
{
// Do not copy static class properties
if (temp.GetAccessors(true)[0].IsStatic)
{
continue;
}
generator.Emit(OpCodes.Ldarg_1);// los
generator.Emit(OpCodes.Ldarg_0);// s
generator.Emit(OpCodes.Callvirt, temp.GetMethod);
generator.Emit(OpCodes.Callvirt, temp.SetMethod);
}
generator.Emit(OpCodes.Ret);
var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
CachedIl[typeof(T)] = clone;
clone(source, los);
}
private static Dictionary<Type, Delegate> CachedIl {
set; get; } = new Dictionary<Type, Delegate>();
}
We need to pay attention to , The copy here just copies the properties of the class , The properties of the class are not copied . If there is a type TestA1
, Look at the following code .
public class TestA1
{
public string Name {
get; set; }
}
So after executing the following code , Got TestA1
It's the same .
public class Foo
{
public string Name {
get; set; }
public TestA1 TestA1 {
get; set; }
}
var foo = new Foo()
{
Name = "123",
TestA1 = new TestA1()
{
Name = "123"
}
};
var foo1 = new Foo();
Clone.CloneObjectWithIL(foo, foo1);
foo1.TestA1.Name == foo.TestA1.Name
foo.Name = "";
foo.TestA1.Name = "lindexi";
foo1.TestA1.Name == foo.TestA1.Name
So when can the above code be used ? In fact, if you need to copy the properties of the base class in a created class , So it's good to use this method , For example, in Model
Will create some classes , And in the ViewModel
Sometimes you need to add some properties to these classes , Such as Checked
, Then you need to replicate Model
Properties of , If you need to write your own attributes, copy them , So the development speed is too slow . So you can use this method at this time .
For example, the base class is Base
, The inheritance class is Derived
, Look at the following code
public class Base
{
public string BaseField;
}
public class Derived : Base
{
public string DerivedField;
}
Base base = new Base();
//some alother code
Derived derived = new Derived();
CloneObjectWithIL(base, derived);
If you need to copy a class to a new class , You can use this code
private static T CloneObjectWithIL<T>(T myObject)
{
Delegate myExec = null;
if (!_cachedIL.TryGetValue(typeof(T), out myExec))
{
// Create ILGenerator
DynamicMethod dymMethod = new DynamicMethod("DoClone", typeof(T), new Type[] {
typeof(T) }, true);
ConstructorInfo cInfo = myObject.GetType().GetConstructor(new Type[] {
});
ILGenerator generator = dymMethod.GetILGenerator();
LocalBuilder lbf = generator.DeclareLocal(typeof(T));
//lbf.SetLocalSymInfo("_temp");
generator.Emit(OpCodes.Newobj, cInfo);
generator.Emit(OpCodes.Stloc_0);
foreach (FieldInfo field in myObject.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic))
{
// Load the new object on the eval stack... (currently 1 item on eval stack)
generator.Emit(OpCodes.Ldloc_0);
// Load initial object (parameter) (currently 2 items on eval stack)
generator.Emit(OpCodes.Ldarg_0);
// Replace value by field value (still currently 2 items on eval stack)
generator.Emit(OpCodes.Ldfld, field);
// Store the value of the top on the eval stack into the object underneath that value on the value stack.
// (0 items on eval stack)
generator.Emit(OpCodes.Stfld, field);
}
// Load new constructed obj on eval stack -> 1 item on stack
generator.Emit(OpCodes.Ldloc_0);
// Return constructed object. --> 0 items on stack
generator.Emit(OpCodes.Ret);
myExec = dymMethod.CreateDelegate(typeof(Func<T, T>));
_cachedIL.Add(typeof(T), myExec);
}
return ((Func<T, T>)myExec)(myObject);
}
C# Wildcard employment confirmation rule
You can use the following code to convert a wildcard to a regular string
public static class WildcardRegexString
{
/// <summary>
/// Wildcard employment confirmation rule
/// </summary>
/// <param name="wildcardStr"></param>
/// <returns></returns>
public static string GetWildcardRegexString(string wildcardStr)
{
Regex replace = new Regex("[.$^{\\[(|)*+?\\\\]");
return replace.Replace(wildcardStr,
delegate (Match m)
{
switch (m.Value)
{
case "?":
return ".?";
case "*":
return ".*";
default:
return "\\" + m.Value;
}
}) + "$";
}
}
Files are often case insensitive , So you need to write a function to tell the user , No case sensitivity is required .
/// <summary>
/// Get the regular name of the wildcard
/// </summary>
/// <param name="wildcarStr"></param>
/// <param name="ignoreCase"> Ignore case </param>
/// <returns></returns>
public static Regex GetWildcardRegex(string wildcarStr, bool ignoreCase)
{
if (ignoreCase)
{
return new Regex(GetWildcardRegexString(wildcarStr));
}
return new Regex(GetWildcardRegexString(wildcarStr), RegexOptions.IgnoreCase);
}
Regular can use assembly mode , Slow start , But it's fast
private static Regex _regex = new Regex("[.$^{\\[(|)*+?\\\\]", RegexOptions.Compiled);
My software needs to be reused , So use this .
C# Enumeration to string
Sometimes you need to convert an enumeration to a string , So how to convert enumeration to string ?
Enumeration to string
If you need to convert enumeration to string , You can convert him directly , Please look at the code.
public enum Di
{
/// <summary>
/// orbital
/// </summary>
Railway,
/// <summary>
/// The river
/// </summary>
River,
}
static void Main(string[] args)
{
Console.WriteLine(Di.Railway.ToString());
}
In this way, you can convert the enumeration to a string
Except for this method , have access to C# 6.0
Key words of , Please look at the code.
Console.WriteLine(nameof(Di.Railway));
String to enumeration
If you convert an enumeration to a string , So how to convert a string to an enumeration ? have access to Enum.Parse
However, this method can throw exceptions , So you need to know that the string can be converted to
public enum Di
{
/// <summary>
/// orbital
/// </summary>
Railway,
/// <summary>
/// The river
/// </summary>
River,
}
static void Main(string[] args)
{
string str = Di.Railway.ToString();
Console.WriteLine(Enum.Parse(typeof(Di), str).ToString());
}
If for an uncertain string , Include empty values , May adopt TryParse
Method
if (Enum.TryParse(typeof(Di),null,out var value))
{
}
The above code will only return false
There will be no prompt that cannot be converted
C# Find
vs FirstOrDefault
This article tells you , When getting the first element of the array , Which method performs better .
Need to know , Both methods are Linq Methods , Before use, you need to reference Linq . about List And so on are inherited enumerable Enumerable
At this point, to get the first element, you can use FirstOrDefault
. If you use Find
Then the type of array required is IList
.
Let's write a simple example
Decompile Find
You can see the following code , The following code deletes some code , Make it easier for everyone to see Find
It uses for
Then use judgment
private T[] _items;
public T Find(Predicate<T> match)
{
for (int index = 0; index < this._size; ++index)
{
if (match(this._items[index]))
return this._items[index];
}
return default (T);
}
and FirstOrDefault
Your code does not exist foreach
, This will call the... Of the list GetEnumerator
Method , And it will call... At the end Dispose
. such FirstOrDefault
Is better than Find
A little bit worse .
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach (TSource source1 in source)
{
if (predicate(source1))
return source1;
}
return default (TSource);
}
So for List
Type to get the first or default, please use Find
, For others, please use FirstOrDefault
- about
List
, Usefor
The speed offoreach
Twice as many - Traverse
array
The speed is traversalList
Twice as many - Use
for
Traversearray
The speed is usingforeach
TraverseList
Of 5 times
版权声明
本文为[Wind god Shura envoy]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204232039329901.html
边栏推荐
- Recommended usage scenarios and production tools for common 60 types of charts
- Go zero framework database avoidance Guide
- The more you use the computer, the slower it will be? Recovery method of file accidental deletion
- Fastdfs思维导图
- Leetcode 1351. Negative numbers in statistical ordered matrices
- Use of node template engine
- Some basic knowledge of devexpress report development
- 电脑越用越慢怎么办?文件误删除恢复方法
- How can matlab obtain the truncated image in trainingimagelabeler
- Devaxpress report replay: complete the drawing of conventional two-dimensional report + histogram + pie chart
猜你喜欢
上海回应“面粉官网是非法网站”:疏于运维被“黑”,警方已立案
go defer
Cmake project under vs2019: calculating binocular parallax using elas method
A login and exit component based on token
【SQL】字符串系列2:将一个字符串根据特定字符分拆成多行
Flex layout
Devexpress 14.1 installation record
Identifier CV is not defined in opencv4_ CAP_ PROP_ FPS; CV_ CAP_ PROP_ FRAME_ COUNT; CV_ CAP_ PROP_ POS_ Frames problem
Some basic knowledge of devexpress report development
MySQL基础之写表(创建表)
随机推荐
Selenium displays webdriverwait
MySQL基础之写表(创建表)
LeetCode-279-完全平方数
缓存淘汰算法初步认识(LRU和LFU)
Create vs project with MATLAB
[stack and queue topics] - sliding window
bounding box iou
Unity Odin ProgressBar add value column
3-5通过XSS获取cookie以及XSS后台管理系统的使用
Unity solves Z-fighting
LeetCode 542、01 矩阵
MySQL stored procedures and functions
[PTA] l1-002 printing hourglass
Shanghai responded that "flour official website is an illegal website": neglect of operation and maintenance has been "hacked", and the police have filed a case
A login and exit component based on token
XXXI` Prototype ` displays prototype properties and`__ proto__` Implicit prototype properties
Preliminary understanding of cache elimination algorithm (LRU and LFU)
"Meta function" of tidb 6.0: what is placement rules in SQL?
DOS command of Intranet penetration
Leetcode 232, queue with stack