zoukankan      html  css  js  c++  java
  • C# 很少人知道的科技

    本文来告诉大家在C#很少有人会发现的科技。即使是工作了好多年的老司机也不一定会知道,如果觉得我在骗你,那么请看看下面

    因为C#在微软的帮助,已经从原来很简单的,到现在的很好用。在10多年,很少人知道微软做了哪些,我在网上找了很多大神的博客,然后和很多大神聊天,知道了一些科技,所以就在这里说。如果大家看到这个博客里面没有的科技,请告诉我。

    无限级判断空

    在 C# 6.0 可以使用??判断空,那么就可以使用下面代码

                var v1 = "123";
                string v2 = null;
                string v3 = null;
    
                var v = v1 ?? v2 ?? v3;
    

    实际上可以无限的使用??

    using 省略长的定义

    例如有这个代码,使用了很多的 List ,里面有很多定义

    var foo =
                    new System.Collections.Generic.Dictionary<
                        System.Collections.Generic.List<System.Collections.Generic.List<string>>, string>();
    

    可以看到需要写很多代码,如果这个值作为参数,才是可怕。

    一个简单的方法是使用 using HvcnrclHnlfk= System.Collections.Generic.Dictionary<System.Collections.Generic.List<System.Collections.Generic.List<string>>,string>; ,这个文件里的所有定义都可以使用 using 后面的值可以代替。

    var foo = new HvcnrclHnlfk();
    

    辣么大

    实际上我有些不好意思,好像刚刚说的都是大家都知道的,那么我就要开始写大家很少知道

              Func<string,string, EventHandler> foo = (x, y) => (s, e) =>
                {
                    var button = (Button) s;
                    button.Left = x;
                    button.Top = y;
                };
    
                Button1.Click += foo(0, -1);
    

    看一下就知道这个定义可以做什么

    千万不要告诉我,你觉得上面编译不通过,实际上我用了黑科技

    但是不告诉你

    冲突的类型

    如果遇到两个命名空间相同的类型,很多时候都是把命名空间全写

    var webControl = new System.Web.UI.WebControls.Control();
    var formControl = new System.Windows.Forms.Control();
    

    如果经常使用这两个控件,那么就需要写空间,代码很多,但是微软给了一个坑,使用这个可以不用写空间

    using web = System.Web.UI.WebControls;
    using win = System.Windows.Forms;
    
    web::Control webControl = new web::Control();
    win::Control formControl = new win::Control();
    

    参见:https://stackoverflow.com/a/9099/6116637

    extern alias

    如果使用了两个 dll ,都有相同命名空间和类型,那么如何使用指定的库

    //a.dll
    
    namespace F
    {
    	public class Foo
    	{
    
    	}
    }
    
    //b.dll
    
    namespace F
    {
    	public class Foo
    	{
    		
    	}
    }
    
    

    这时就可以使用 extern alias

    参见:C#用extern alias解决两个assembly中相同的类型全名 - fresky - 博客园

    字符串

    大家看到了 C# 6.0 的$,是不是可以和@一起?

                var str = "kktpqfThiq";
                string foo = $@"换行
    {str}";
    

    注意两个的顺序,反过来直接告诉你代码不能这样写

    表达式树获取函数命名

    定义一个类,下面通过表达式树从类获得函数命名

        class Foo
        {
            public void KzcSevfio()
            {
            }
        }
    
           static void Main(string[] args)
            {
                GetMethodName<Foo>(foo => foo.KzcSevfio());
            }
    
            private static void GetMethodName<T>(Expression<Action<T>> action) where T : class
            {
                if (action.Body is MethodCallExpression expression)
                {
                    Console.WriteLine(expression.Method.Name);
                }
            }
    

    这样就可以拿到函数的命名

    特殊关键字

    实际上有下面几个关键字是没有文档,可能只有垃圾微软的编译器才知道

    __makeref
    
    __reftype
    
    __refvalue
    
    __arglist
    

    不过在 C# 7.2 都可以使用其他的关键字做到一些,详细请看我的 C# 7.0 博客

    DebuggerDisplay

    如果想要在调试的时候,鼠标移动到变量显示他的信息,可以重写类的 ToString

        public sealed class Foo
        {
            public int Count { get; set; }
    
            public override string ToString()
            {
                return Count.ToString();
            }
        }
    

    但是如果 ToString 被其他地方用了,如何显示?

    垃圾微软告诉大家,使用 DebuggerDisplay 特性

        [DebuggerDisplay("{DebuggerDisplay}")]
        public sealed class Foo
        {
            public int Count { get; set; }
    
            private string DebuggerDisplay => $"(count {Count})";
        }
    

    他可以使用私有的属性、字段,使用方法很简单

    参见Using the DebuggerDisplay Attribute

    使用 Unions (C++ 一样的)

    如果看到 C++ 可以使用内联,不要说 C# 没有,实际上也可以使用 FieldOffset ,请看下面

    [StructLayout(LayoutKind.Explicit)]
    public class A
    {
        [FieldOffset(0)]
        public byte One;
    
        [FieldOffset(1)]
        public byte Two;
    
        [FieldOffset(2)]
        public byte Three;
    
        [FieldOffset(3)]
        public byte Four;
    
        [FieldOffset(0)]
        public int Int32;
    }
    

    这时就定义了int变量,修改他就是修改其他的三个

         static void Main(string[] args)
        {
            A a = new A { Int32 = int.MaxValue };
    
            Console.WriteLine(a.Int32);
            Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);
    
            a.Four = 0;
            a.Three = 0;
            Console.WriteLine(a.Int32);
        }
    

    这时会输出

    2147483647
    FF FF FF 7F
    65535
    

    接口默认方法

    实际上可以给接口使用默认方法,使用的方式

    public static void Foo(this IF1 foo)
    {
         //实际上大家也看到是如何定义
    }
    

    数字格式

    string format = "000;-#;(0)";
    
    string pos = 1.ToString(format);     // 001
    string neg = (-1).ToString(format);  // -1
    string zer = 0.ToString(format);     // (0)
    

    参见:自定义数字格式字符串

    stackalloc

    实际上很多人都不知道这个,这是不安全代码,从栈申请空间

    int* block = stackalloc int[100]; 
    

    参见:stackalloc

    调用堆栈

    如果需要获得调用方法的堆栈,可以使用这个文章的方法

      class Program
        {
            static void Main(string[] args)
            {
                var foo = new Foo();
                foo.F1();
            }
        }
    
        public sealed class Foo
        {
            public void F1()
            {
                F2();
            }
    
            void F2()
            {
                var stackTrace = new StackTrace();
                var n = stackTrace.FrameCount;
                for (int i = 0; i < n; i++)
                {
                    Console.WriteLine(stackTrace.GetFrame(i).GetMethod().Name);
                }
            }
        }
    

    输出

    F2
    F1
    

    参见:WPF 判断调用方法堆栈

    指定编译

    如果使用 Conditional 可以让代码在指定条件不使用,我写了这个代码,在 Release 下就不会使用 F2

        public sealed class Foo
        {
            public Foo F1()
            {
                Console.WriteLine("进入F1");
                return this;
            }
    
            [Conditional("DEBUG")]
            public void F2()
            {
                Console.WriteLine("F2");
            }
        }
    

    简单让代码跑一下

            static void Main(string[] args)
            {
                var foo = new Foo();
                foo.F1();
                foo.F2();
            }
    

    结果是什么,大家也知道,在 Debug 和 Release 输出是不相同。但是这么简单的怎么会在这里说,请大家看看这个代码输出什么

         static void Main(string[] args)
            {
                var foo = new Foo();
                foo.F1().F2();
            }
    

    实际上在 Release 下什么都不会输出,F1 不会被执行

    true 判断

    下面写个见鬼的代码

                var foo = new Foo(10);
    
                if (foo)
                {
                    Console.WriteLine("我的类没有继承 bool ,居然可以这样写");
                }
    

    没错 Foo 没有继承 bool 居然可以这样写

    实际上就是重写 true ,请看代码

        public class Foo
        {
            public Foo(int value)
            {
                _count = value;
            }
    
            private readonly int _count;
    
            public static bool operator true(Foo mt)
            {
                return mt._count > 0;
            }
    
            public static bool operator false(Foo mt)
            {
                return mt._count < 0;
            }
        }
    
    

    是不是觉得很多有人这样写,下面让大家看一个很少人会知道的科技,感谢walterlv

    await 任何类型

    await "林德熙逗比";
    
    await "不告诉你";
    

    这个代码是可以编译通过的,但是只有在我的设备,然后看了这个博客,可能你也可以在你的设备编译

    变量名使用中文

    实际上在C#支持所有 Unicode ,所以变量名使用中文也是可以的,而且可以使用特殊的字符

            public string Hu00e5rføner()
            {
                return "可以编译";
            }
    

    if this == null

    一般看到下面的代码都觉得是不可能

    if (this == null) Console.WriteLine("this is null");
    

    如果在 if 里面都能使用 this == null 成立,那么一定是vs炸了。实际上这个代码还是可以运行。

    在一般的函数,如 Foo ,在调用就需要使用f.Foo()的方法,方法里 this 就是 f ,如果 f == null 那么在调用方法就直接不让运行,如何到方法里的判断

    f.Foo(); //如果 f 为空,那么这里就不执行
    
    void Foo()
    {
       // 如果 this 为空,怎么可以调用这个方法
       if (this == null) Console.WriteLine("this is null");
    }
    

    实际上是可以做的,请看(C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切 - walterlv

    如上面博客,关键在修改callvirt call,直接修改 IL 可以做出很多特殊的写法。

    那么这个可以用在哪里?可以用在防止大神反编译,如需要使用下面逻辑

    //执行的代码
    
    //不执行的代码
    
    if(true)
    {
       //执行的代码
    }
    else
    {
       //不执行的代码 
    }
    

    但是直接写 true 很容易让反编译看到不使用代码,而且在优化代码会被去掉,所以可以使用下面代码

    if(this == null)
    {
       //执行的代码
    }
    else
    {
       //不执行的代码 
    }
    

    知识共享许可协议
    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

  • 相关阅读:
    UVA 11174 Stand in a Line,UVA 1436 Counting heaps —— (组合数的好题)
    UVA 1393 Highways,UVA 12075 Counting Triangles —— (组合数,dp)
    【Same Tree】cpp
    【Recover Binary Search Tree】cpp
    【Binary Tree Zigzag Level Order Traversal】cpp
    【Binary Tree Level Order Traversal II 】cpp
    【Binary Tree Level Order Traversal】cpp
    【Binary Tree Post order Traversal】cpp
    【Binary Tree Inorder Traversal】cpp
    【Binary Tree Preorder Traversal】cpp
  • 原文地址:https://www.cnblogs.com/lindexi/p/cs-few-people-will-be-able.html
Copyright © 2011-2022 走看看