Rocho.J

人脑是不可靠的, 随时记录感悟并且经常重复!

 

[转] C#中的奇淫技巧 --- 转自: https://www.zhihu.com/question/27421302

内容
 
C# 有什么奇技淫巧?修改
写补充说明
举报 添加评论 分享 • 邀请回答
默认排序
按时间排序
33 个回答

83
赞同反对,不会显示你的姓名
王韦恩卑鄙 因为美术苦手 只好去做框架 萌大叔
83 人赞同
我觉得 泛基是一种很棒的技巧 这个名字是和 @赵劼 一同起的。

public abstract class MyBase<T> where T :MyBase <T> //被人肉编译器 @vczh 发现少写了<T>

{

	public static string DataForThisType {get;set;}
	public static T Instance{get;protected set;}
	//老赵说的邪恶的部分:让父类操作子类出现了
	public static readonly IReadOnlyDictionary<string,MemberInfo> Members
		=typeof(T)
			.GetMembers()
			.ToDictionary(x=>x.Name);

}



public class MyClass:MyBase<MyClass>
{

	static MyClass()
	{ 
		DataForThisType =string.Format(
			"MyClass got {0} members",Members.Count);
		Instance = new MyClass();
	}

}

子类的static 成员互相不干涉


@赵劼
简单说就是让父类可以静态化地知道当前子类…
------------

补充用途:

1 你有时候希望在父类规定一些行为,让子类无法修改,但是这些实现是依赖一个子类才能获取的值,你又不可能知道所有的子类 ,没办法替它在父类里面初始化,这时候就需要在父类里面定义一个每个子类一个的,但又是静态的空间。

2 你需要每个子类都有一些公开的静态成员,这些成员的类型是子类类型

3 在不知道子类具体类型的情况下,让父类利用泛型参数先替未来的子类做一些事情。


-----

貌似能实现这样功能的其他语言不多。 可是我没有挨个试验过 有人能有反馈告诉我自己熟悉的语言能否做到的话,感激不尽。

编辑于 2015-01-07 40 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

23
赞同反对,不会显示你的姓名
Ivony编程话题优秀回答者 C# 程序员一枚
23 人赞同
1、泛型类型字典,这个已经基本不算奇技淫巧了,因为大家都在用:
    private static class ConverterCache<T>
    {
      public static IDbValueConverter<T> ConverterInstance { get; set; }
    }
上面是随便从我一个项目里面摘出来的最简单的类型字典的例子,一个最简单的类型字典只需要一个泛型类,和一个静态字段就够了。

详细请参考:
泛型技巧系列:类型字典和Type Traits


2、利用using来做Scope,其实我个人不是很喜欢这个技巧,在http://ASP.NET MVC里面广泛使用:
using( Html.BeginForm() )
{
  //...
}

3、其实扩展方法可以做很多好玩的东西:
public static T CastTo<T>( this object obj )
{
  return (T) obj;
}

public static T IfNull<T>( this T value, T defaultValue )
{
  if ( value == null || Convert.IsDBNull( value ) )
    return defaultValue;
  else
    return value;
}

我还有一个扩展方法把一个类型所有属性和属性值转换成一个Dictionary的,代码就不贴了,除了一些常规用途,有时候初始化一个Dictionary很麻烦的时候,还可以直接new一个匿名对象,再用这个扩展方法转成Dictionary就完了。

4、运算符重载
运算符重载理论上不算什么奇技淫巧,是个标准特性,但不知道为什么用的人特别少。

var logger = new ConsoleLogger() + new TextFileLogger( @"C:\Temp\Logs\1.log" );
我的LogUtility,可以将多个Logger用+连接起来,一并记录日志。


5、dynamic绑定
dynamic说白了就是运行时绑定,而且绑定到什么逻辑上是可以在运行时再根据上下文重新定义的。劣势是没有智能提示,但有些地方根本不需要智能提示,或者没有强类型,例如数据绑定。

我们现在的页面数据绑定已经开始倾向于大量使用dynamic,这样一来页面数据绑定便可以先行,根本用不着更新组件接口神马的,需要什么东西直接绑,反正不到运行时不会检测这个东西是不是存在。
而运行时真正执行这个绑定的时候,因为是代码逻辑,所以可以玩出很多花样。

最简单的花样像是大小写不敏感,绑定name属性,找到了一个叫做Name的属性,把值给他就好了。

复杂一点的花样,例如找不到Name属性,就输出一个字符串"[Name]"然后页面上就看到:
姓名:[Name]
这样一来,前端页面原型完全可以演示,根本不用等后面的数据对接,啥时候对接完了,自然变成最终的数据。

再复杂一点的,譬如有个属性改名了,原来的名字不存在了,没事,映射到新的名字,维护一个改名兼容表就完了。


其他的想到再补充。
编辑于 2015-01-07 10 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

17
赞同反对,不会显示你的姓名
iyomumx
17 人赞同
基本上看SO的这个问题就够了:
tips and tricks
给__arglist等关键字添个示例吧:
class Program
{
    [DllImport("msvcrt.dll")]
    public static extern int printf(string format, __arglist);
    
    [DllImport("msvcrt.dll")]
    public static extern int scanf(string format, __arglist);
    
    public static void WriteTypes(__arglist)
    {
        ArgIterator ai = new ArgIterator(__arglist);
        while(ai.GetRemainingCount() >0)
        {
            TypedReference tr = ai.GetNextArg();
            Console.WriteLine("Type:{0}", __reftype(tr));
            Console.WriteLine("Value:{0}", __refvalue(tr, int));
        }
    }
    
    static void Main(string[] args)
    {
        int x, y;
        scanf("%d%d", __arglist(out x, out y));
        printf("hello %d\nhello %x\n", __arglist(x, y));
        WriteTypes(__arglist(x, y));
    }
}
编辑于 2015-01-06 3 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

21
赞同反对,不会显示你的姓名
很好 金融软件工作者
21 人赞同
SIMD支持
居然没有提到最新的SIMD指令支持啊,这个貌似碉堡:
// 两个整数数组相加的常规方法
for (int i = 0; i < size; i++) 
{ 
    C[i] = A[i] + B[i]; 
} 
// SIMD 方法, 每次几个元素同时相加,Vector<int>.Count是每个SSE/AVX寄存器容纳int的个数
using System.Numerics.Vectors;
for (int i = 0; i < size; i += Vector<int>.Count) 
{ 
    Vector<int> v = new Vector<int>(A,i) + new Vector<int>(B,i); 
    v.CopyTo(C,i); 
} 
更碉堡的是,Vector方法是硬件自适应的。也就是说,如果你的硬件只支持SSE2,就每四个int相加,如果支持AVX2,就每8个相加。

无GC模式
调用GC.TryStartNoGCRegion(int64)函数,传入一个内存大小(比如1G)。CLR会开辟一个指定大小的内存区域,然后进入无GC模式。适用于critical path部分的业务逻辑。
编辑于 2015-07-23 6 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

216
赞同反对,不会显示你的姓名
Trotyl Yu 某外包公司的搬砖工。
216 人赞同
C# 6 已经在随VS2015 RC正式发布。。有兴趣的童鞋可以观看在Build 2015中的介绍视频。。
What's New in C# 6 and Visual Basic 14

里面有很多本文中没有提到的特性哟。。

BTW:C# 7的功能清单已经在出了。。
C# 7 Work List of Features · Issue #2136 · dotnet/roslyn · GitHub


--------我是华丽的分割线--------

稍微列举一下 C# 6 中将要出现的奇技淫巧吧~
参考自 New Features in C# 6 ,有删改~

作为一门专为程(yu)序(fa)员(tang)考虑的语言,感受一下来自微软的满满的恶意...

1. 字符串内联
在之前的版本中,常用的格式化字符串:
var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age);
在 C# 6 中:
//无格式
var s = $"{p.Name} is {p.Age} year{{s}} old";

//带格式
var s = $"{p.Name,20} is {p.Age:D3} year{{s}} old";

//带子表达式
var s = $"{p.Name} is {p.Age} year{(p.Age == 1 ? "" : "s")} old";

2. 空条件运算符
在之前的版本中对于 可空类型 或 动态类型 ,获取子元素往往较为复杂:
if(someSchool != null && someSchool.someGrade != null && someSchool.someGrade.someClass != null)
{
    return someSchool.someGrade.someClass.someOne;
}
在 C# 6 中,引入了新的运算符:
return someSchool?.someGrade?.someClass?.someOne;
//也可以使用下标运算,例如
//return someArray?[0];
如果 ?. 运算符的左项为 null ,则直接返回 null 。
对于方法或者委托的执行,可以使用 Invoke:
someMethod?.Invoke(args);

3. nameof 表达式
可以直接返回传入变量的名称,而无需复杂的反射。
int someInt;
Console.WriteLine(nameof(someInt)); //"someInt"
注:如果是前面带有命名空间和/或类名,只返回最后的变量名。

4. 索引初始化器
在 C# 6 中简化了对 Dictionary 的初始化方法:
var numbers = new Dictionary<int, string> { 
    [7] = "seven", 
    [9] = "nine", 
    [13] = "thirteen" 
};

5. 条件异常处理
在 C# 6 中可以选择性地对某些异常进行处理,无需额外增加判断过程:
try { … } 
catch (MyException e) if (myfilter(e)) 
{ 
    … 
}

6. 属性初始化器
在 C# 6 中可以直接对属性进行初始化:
public class Customer 
{ 
    public string First { get; set; } = "Jane"; 
    public string Last { get; set; } = "Doe"; 
}
以及可以类似定义只读的属性。

7. 成员函数的 lambda 定义
在 C# 6 中可以使用 lambda 表达式来定义成员方法。
public class Point
{
    public Point Move(int dx, int dy) => new Point(x + dx, y + dy); 
    public static Complex operator +(Complex a, Complex b) => a.Add(b); 
    public static implicit operator string(Person p) => $"{p.First}, {p.Last}";
}

8. 结构体的无参构造函数
在 C# 6 中可以创建结构体的不带参数的构造函数。
struct Person 
{ 
    public string Name { get; } 
    public int Age { get; } 
    public Person(string name, int age) { Name = name; Age = age; } 
    public Person() : this("Jane Doe", 37) { } 
}

9. using 静态类
在 C# 6 中 using 除了可以用于命名空间也可以用于静态类。
using System.Console; 
using System.Math;

class Program 
{ 
    static void Main() 
    { 
        WriteLine(Sqrt(3*3 + 4*4)); 
    } 
}

大体感觉主要就是这些内容... (o=>_<=o)
编辑于 2016-02-23 54 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

8
赞同反对,不会显示你的姓名
hillin
8 人赞同
两个关于接口的小技巧:


伪多继承

比方说扫帚已经是清洁工具了,我又想把它纳入武器范畴:

class Broom : CleaningTool, IWeapon
{
    double IWeapon.Damage { get { return 42.0; } }
}

interface IWeapon
{
    double Damage { get; }
}

static class IWeaponExtensions
{
    public static void Attack(this IWeapon @this, ITarget target)
    {
        target.Hitpoint -= @this.Damage;
    }
}

Internal Interface

你有一系列实现了同一接口的类,但希望为这个接口增加一个只在当前程序集内可以调用的内部成员。
比如说:
public interface ISomething
{
    void DoSomething();
}

public class A : ISomething
{
    public void DoSomething() { }
}

public class B : ISomething
{
    public void DoSomething() { }
}
我们要给ISomething增加一个方法GetDebugString()。因为是调试用的,所以不希望外部程序来访问它。但是接口里不能声明成员的访问性,怎么办呢?这时候就可以用内部接口:
public interface ISomething
{
    void DoSomething();
}

internal interface IInternalSomething : ISomething
{
    string GetDebugString();
}

public class A : IInternalSomething 
{
    public void DoSomething() { }
    string IInternalSomething.GetDebugString() { return "A"; }
}

public class B : IInternalSomething 
{
    public void DoSomething() { }
    string IInternalSomething.GetDebugString() { return "B"; }
}
这样就可以在程序集内部使用IInternalSomething来替代ISomething来实现这一需求。由于A和B类里面的GetDebugString()是explicit实现,所以在程序集外部也不会被访问到。
编辑于 2016-12-21 5 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

37
赞同反对,不会显示你的姓名
王俊杰 flag狂魔
37 人赞同
能写一行代码绝不写两行!

以下两个变量都可能为NULL
     object a = null;
     string b = null;
按照我以前的写法就需要这样.....
    if (a == null ) //或者是 b
    {
       a1 = "";
    }
    else
    {
       a1 = a.ToString();
    }
后来改成三元运算符...
a1 = a == null ? "" : a.ToString();

再后来看书学了一招...
  object a1 = a ?? "";
  string b1 = b ?? "";
//或者
  string a1 =(a ?? "").ToString()

再后来无意间反编译自己以前写的程序发现了目前最爱的用法:
string a1 = a + "";
管你是什么类型,返回都是字符串,NULL 也不会报错的。
因为....

如果不计较性能,这也许是我最值得炫耀的简化成果了。

------------------------2015-01-10----------------------------------
string a1 = a + "";
上面的情况是当a类型为 string 的情况。
当a是 object 时会被编译成 string a1 = string.Concat(a);
至于string.Concat方法...
编译器就是赞~

补一个博客!
不计代价的简化代码
编辑于 2015-12-18 25 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

17
赞同反对,不会显示你的姓名
RednaxelaFX编程、编译原理、编程语言等 7 个话题优秀回答者 软…
17 人赞同
来发俩完全没用纯好玩的奇技淫巧,纯原创喔(因为没有其他人这么无聊蛋疼搞这种事情吧…)
1、要让CLR挂掉的话(第二弹)……
2、LINQ与DLR的Expression tree(5):用lambda表达式表示常见控制结构
发布于 2015-01-06 2 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

2
赞同反对,不会显示你的姓名
Elendil Zheng 假装是投行的
2 人赞同
当我有一次给小伙伴们填一个坑的时候用了[Obsolete("This property is duplicated with XXX",true)]的时候 我的小伙伴们都惊呆了
发布于 2015-04-08 5 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

3
赞同反对,不会显示你的姓名
陆建新 一个程序员
3 人赞同
想不起来C#有哪些称得上奇技淫巧的了。

不过印象比较深的是13年的NDC (Norwegian Developers Conference)上,C# in Depth的作者Jon Skeet做了一个"Abusing C#“的演讲。

视频地址:http://vimeo.com/68320506

简介:
We've all seen bad code. Code worthy of the Daily WTF. Code which makes us wonder how products ever ship, let alone work. Bad code is boring. Evil code is entirely different. It's bending a language in ways that would make the designers weep. It's code which you stare at and swear that it can't possibly work... until you see how it does.

I've got some evil code. I'll show it to you. You'll wish I hadn't.


没有赞,还是挑一个最简单的例子。

class BasicDemo
{
        static void Main(string[] args)
        {
            var funky = new[] { "hello", "world", "how", "are", "you" };
            var query = funky.Evil() - "world" + "today" 
                        & (x => x.Length == 5)
                        | (x => x.ToUpper());

            Console.WriteLine(query * 3);

            var xor1 = new[] { "foo", "bar", "baz" }.Evil();
            var xor2 = new[] { "qux", "bar" }.Evil();
            Console.WriteLine(xor1 ^ xor2);
        }
}

输出结果是:

HELLO, TODAY, HELLO, TODAY, HELLO, TODAY
foo, baz, qux

猜猜为啥有这样的结果?答案会在评论里。

编辑于 2015-01-07 2 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

3
赞同反对,不会显示你的姓名
羽毛 可爱的男孩子,和服控,想学会穿振袖
3 人赞同
可以利用 Linq 的查询表达式写一个 Parser,可参考 Monadic Parser Combinators using C# 3.0
发布于 2015-01-06 添加评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

3
赞同反对,不会显示你的姓名
王浩 思/想 家!
3 人赞同
成员用中文命名算不算?
发布于 2015-04-09 2 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

2
赞同反对,不会显示你的姓名
羊牮 油女一族,优秀的Bug制造者。⎝
2 人赞同
一、
把任意类型字典写在xaml里,如:
    [Serializable]
    public class IntDictionary : Dictionary<string, int>
    {
        public IntDictionary()
        { }
        protected IntDictionary(SerializationInfo info, StreamingContext context)
            : base(info, context)
        { }
    }
Xaml 里可以填(classes和system为空间名,自己添加到根元素去):
    <classes:IntDictionary x:Key="LstEvents">
        <system:Int32 x:Key="alarm">1</system:Int32>
        <system:Int32 x:Key="cancel">2</system:Int32>
        <system:Int32 x:Key="Synchronization">4</system:Int32>
        <system:Int32 x:Key="disarm">21</system:Int32>
        <system:Int32 x:Key="arming">22</system:Int32>
        <system:Int32 x:Key="unlock">60</system:Int32>
        <system:Int32 x:Key="call center">61</system:Int32>
        <system:Int32 x:Key="talking">62</system:Int32>
        <system:Int32 x:Key="end dialog">63</system:Int32>
        <system:Int32 x:Key="calling">150</system:Int32>
        <system:Int32 x:Key="watching">152</system:Int32>
    </classes:IntDictionary>

在App.Xaml 中引用该 Xaml ,以后代码里就可以通过 Application.Current.FindResource 获取。亮点是,1、字典静态声明,2、字典可以同时被代码和xaml调用。

二、不是实现了 IEumerable接口的类型才能使用Linq,只要你为某个容器实现了相关函数,即使是扩展函数也可以,譬如,你为CC容器实现了Select函数,那么对于CC类的对象cc,你就可以使用
from x in cc select x.xo
编辑于 2015-02-05 1 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

0
赞同反对,不会显示你的姓名
赵文君 程序员
给自定义值类型的构造函数里面赋值应该算一个了。
struct My
{
	private readonly string _a;
	
	private readonly string _b;
	
	public My(string a, string b)
	{
		this._a = a;
		this._b = b;
	}
	
	public My(string a)
	{
		// This!
		this = new My();
		this._a = a;
	}
}
那句this = new My();比较奇葩,不写会编译错。
不这样写就得手动把所有成员赋值。
注意:引用类型不能这样写。
发布于 2016-05-26 添加评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

4
赞同反对,不会显示你的姓名
陈学辉 C#/Java
4 人赞同
async/await异步机制。
yield语法糖。
linq延迟查询。
property成员。
发布于 2015-01-06 1 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

8
赞同反对,不会显示你的姓名
姬宇航
8 人赞同
C#每一个版本增加的新特性对别的语言的开发者来说都算是奇技淫巧了.
C#3的LINQ
C#4的dynamic
C#5的async/await
C#6利用Roslyn,肯定会出现更多的花样,想想都期待啊.
发布于 2015-01-06 4 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

0
赞同反对,不会显示你的姓名
许荣雨 微信号:unity3ds,加了一起吹牛逼。
IEnumerator  Do ()
{ 
While(true){
 Console.WriteLine("哈哈哈");
 yield return new WaitForSeconds  (2); 
 Console.WriteLine("哈哈哈");
 }
}

unity 里的c#
发布于 2016-01-22 7 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 申请转载
高小 程序员
1 人赞同
看了帖子发现是去年的,手有点儿痒了,发现没有人写关于C#异步处理流的,我贴一个。

using System;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

using static System.Console;

namespace ConsoleApplication3
{
    class Program
    {
        private static BufferBlock<User> userFlow = new BufferBlock<User>();

        static void Main(string[] args)
        {
            Task.Factory.StartNew(async () => await NewData());
            Task.Factory.StartNew(ProcessData);
            ReadLine();
        }


        private static async Task NewData()
        {
            for (int index = 1; index < 10001; index++)
            {
                userFlow.Post(new User { Name = "张三丰", Number = index });
            }

            await userFlow.Completion;
        }

        private static void ProcessData()
        {
            while (userFlow.Completion.Status != TaskStatus.RanToCompletion)
            {
                User _receiveData = userFlow.Receive();
                _receiveData.IsProcess = (0 == _receiveData.Number % 2);
                WriteLine($"Number : {_receiveData.Number } Name : {_receiveData.Name}  Process : {_receiveData.IsProcess} Process Time : { DateTime.Now }");
            }
        }
    }

    public class User
    {
        public int Number { get; set; }

        public string Name { get; set; }

        public bool IsProcess { get; set; } = false;
    }
}
发布于 2016-05-09 1 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

6
赞同反对,不会显示你的姓名
陈子豪 会写一点C#
6 人赞同
Console.Beep();
发布于 2015-01-31 5 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利

6
赞同反对,不会显示你的姓名
耀德 手速快得不行
6 人赞同
Console.WriteLine("甩了java百条街")
发布于 2015-08-29 11 条评论 感谢 分享 收藏 • 没有帮助 • 举报 • 作者保留权利
View Code

 

posted on 2017-01-23 12:27  RJ  阅读(353)  评论(0)    收藏  举报

导航