当前位置:网站首页>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, UseforThe speed offoreachTwice as many - Traverse
arrayThe speed is traversalListTwice as many - Use
forTraversearrayThe speed is usingforeachTraverseListOf 5 times
版权声明
本文为[Wind god Shura envoy]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204232039329901.html
边栏推荐
- UKFslam
- LeetCode 709、转换成小写字母
- Commande dos pour la pénétration de l'Intranet
- Selenium 显示等待WebDriverWait
- High paid programmer & interview question series 91 limit 20000 loading is very slow. How to solve it? How to locate slow SQL?
- Analysis of the relationship between generalized Bim and CAD under the current background
- Imitation Baidu map realizes the three buttons to switch the map mode by automatically shrinking the bottom
- 上海回應“面粉官網是非法網站”:疏於運維被“黑”,警方已立案
- Matlab matrix index problem
- 41. The first missing positive number
猜你喜欢

Summary and effect analysis of methods for calculating binocular parallax

Go language development Daily Fresh Project Day 3 Case - Press Release System II

Syntax Error: TypeError: this. getOptions is not a function

On IRP from the perspective of source code

Recognition of high-speed road signs by Matlab using alexnet

PHP的Laravel与Composer部署项目时常见问题

LeetCode 116. Populate the next right node pointer for each node

Solution: NPM err! code ELIFECYCLE npm ERR! errno 1

High paid programmer & interview question series 91 limit 20000 loading is very slow. How to solve it? How to locate slow SQL?

Scripy tutorial - (2) write a simple crawler
随机推荐
黑客的入侵方式你知道几种?
What about laptop Caton? Teach you to reinstall the system with one click to "revive" the computer
Leetcode 542, 01 matrix
Preliminary understanding of cache elimination algorithm (LRU and LFU)
Some basic knowledge of devexpress report development
内网渗透之DOS命令
Flex layout
[matlab 2016 use mex command to find editor visual studio 2019]
How to use PM2 management application? Come in and see
go interface
上海回应“面粉官网是非法网站”:疏于运维被“黑”,警方已立案
[PTA] l1-006 continuity factor
LeetCode 20、有效的括号
上海回應“面粉官網是非法網站”:疏於運維被“黑”,警方已立案
Unity solves Z-fighting
bounding box iou
How to do after winning the new debt? Is it safe to open an account online
JS arrow function user and processing method of converting arrow function into ordinary function
GO語言開發天天生鮮項目第三天 案例-新聞發布系統二
Case of the third day of go language development fresh every day project - news release system II