csharp 问题记录
记录一些开发csharp时遇到的问题以及解决方案。
注意隐式转换与构造函数的冲突问题
因为cshapr每次都要写new,所以添加了一些隐式类型转换语法糖. 但是下面的代码就会出现问题, 就是new WildCardPattern(Name)这里其实并不是调用默认的WildCardPattern(string Name, ExprPattern? Pattern), 而是又被隐式类型转换成了WildCardPattern然后准备调用复制构造函数构造,但是隐式类型转换的时候就陷入递归了.
public sealed record WildCardPattern(string Name, ExprPattern? Pattern) : ExprPattern
{
private static int _globalCardIndex = 0;
public WildCardPattern() : this($"wc_{_globalCardIndex++}", null)
{
}
public static implicit operator WildCardPattern(string Name) => new WildCardPattern(Name);
public override bool MatchLeaf(Expr expr) => (Pattern?.MatchLeaf(expr) ?? true) && MatchCheckedType(expr);
}C# delegate的本质
最近在弄一个直接jit生成代码然后用c#动态调用的东西,但是需要动态调用你必须要告诉当前的函数指针一个delegate的定义,不然c#就不知道你要输入什么返回什么. 那么既然是jit,我们的就不能提前写好这个定义, 所以需要动态构造一个delegate.
给定一个类:
public class CustomType
{
public delegate float declf(float x, float y);
}他的delegate的修饰符并不是一种attr,而是一种表示他继承自MulticastDelegate, 所以你可以发现他的是一个NestedType,并且他的基类是MulticastDelegate.
var t = cls_type.GetNestedType("declf");
Assert.Equal(t.BaseType, typeof(MulticastDelegate));同时他还具备了4个方法,其中一个是构造方法,以及三个重写的方法. 
通过检查他的il我们可以发现declf就是个class,所以事情就简化成了构造这个类的问题.:
.class private auto ansi '<Module>'
{
} // end of class <Module>
.class public auto ansi beforefieldinit CustomType
extends [mscorlib]System.Object
{
// Nested Types
.class nested public auto ansi sealed declf
extends [mscorlib]System.MulticastDelegate
{
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
object 'object',
native int 'method'
) runtime managed
{
} // end of method declf::.ctor
.method public hidebysig newslot virtual
instance float32 Invoke (
float32 x,
float32 y
) runtime managed
{
} // end of method declf::Invoke
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult BeginInvoke (
float32 x,
float32 y,
class [mscorlib]System.AsyncCallback callback,
object 'object'
) runtime managed
{
} // end of method declf::BeginInvoke
.method public hidebysig newslot virtual
instance float32 EndInvoke (
class [mscorlib]System.IAsyncResult result
) runtime managed
{
} // end of method declf::EndInvoke
} // end of class declf
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method CustomType::.ctor
} // end of class CustomType接下来就照葫芦画瓢把这个类的定义给生成出来,然后我们再用这个定义的类型拿去binding那个dll里面的函数,然后我们就可以动态的invoke生成的代码了~
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
TypeBuilder tb = mb.DefineType("MyDynamicType", TypeAttributes.Public);
TypeBuilder nesttb = tb.DefineNestedType("DynamicDelegate", TypeAttributes.NestedPublic | TypeAttributes.Sealed, typeof(MulticastDelegate));
var ctor = nesttb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard | CallingConventions.HasThis, new[] { typeof(object), typeof(IntPtr) });
ILGenerator ctorIL = ctor.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldarg_1);
ctorIL.Emit(OpCodes.Ret);
var invoke = nesttb.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(float), new[] { typeof(float), typeof(float) });
var beginInvoke = nesttb.DefineMethod("BeginInvoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(IAsyncResult), new[] { typeof(float), typeof(float), typeof(IAsyncResult), typeof(object) });
var endInvoke = nesttb.DefineMethod("EndInvoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(float), new[] { typeof(IAsyncResult) });
var created_class = tb.CreateType();
return created_class;****dotnet test crash时的解决方案
- 开启–blame以及–blame-crash
- 或者通过环境变量开启dump。
export DOTNET_DbgEnableMiniDump=1
export DOTNET_DbgMiniDumpType=4
export DOTNET_DbgMiniDumpName=/tmp/crash_%p.dmp- 通过lldb+sos插件调试core dump.
- 比如
lldb exec -c xx.dmp。 这个exec可以是python或者dotnet。
- 比如
安装dotnet sos,参考
- dotnet tool install –global dotnet-sos
- dotnet tool install -g dotnet-symbol
- dotnet sos install
sudo cp /Applications/Xcode.app/Contents/Developer/usr/bin/lldb /usr/local/bin
sudo install_name_tool -add_rpath /Applications/Xcode.app/Contents/SharedFrameworks /usr/local/bin/lldb
sudo codesign --force --sign - /usr/local/bin/lldb然后用这个/usr/local/bin/lldb进行调试, 要注意使用的这里的命令 才能看到csharp中的内容。 比如把bt替换成dumpstack
P/invoke中使用AddressSanitizer
我需要调试ffi中的内存引用问题: 1. 首先编译时开启-fsanitize=address -g, 注意得编译为动态库。 2. 在macos中,可以在xcode中找到asan的动态库:
❯ find /Applications/Xcode.app -name '*libclang_rt.asan*osx_dynamic.dylib'
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/17/lib/darwin/libclang_rt.asan_osx_dynamic.dylib- 声明环境变量
export DYLD_INSERT_LIBRARIES=$ASAN_LIB,并执行代码