目录
介绍
使用代码
兴趣点
可以通过C# 项目 (.csproj) 文件中的
C#提供了两种“类型安全的宏”,很像委托作为“类型安全的函数指针”,第一种是扩展方法,第二种是值类型,两者都足够小以至于它们经常被JIT内联,因此“宏”。使用这些小的C#宏,您几乎可以创建任何您想要的东西,并使C#成为您的语言风格。要启动一个新的健壮的C#项目,您需要从一个根接口开始:
using System;
using System.Collections.Generic;
[assembly: CLSCompliant(true)]
namespace Pyramid.Kernel.Up
{
/// The extension method .
public static partial class It { }
/// An .
public partial interface JIt
{
/// An for external 0's.
/// Its output .
partial interface J0
{
/// The 0 .
static readonly IEnumerator ZTo = Z.GetEnumerator();
/// The 0 .
static JOut[] ZArray => Array.Empty();
/// The 0 .
static IEnumerable Z => ZArray;
/// The 0 .
///
static IReadOnlyCollection ZReadOnlyCollection => ZArray;
/// The 0 .
static IReadOnlyList ZReadOnlyList => ZArray;
}
}
/// An autonomous .
/// Its self-referencing .
public partial interface JIt : JIt where J : JIt, new()
{
/// The 0 .
static readonly J Z0 = new();
}
/// An autonomous .
/// Its self-referencing .
[Serializable]
public abstract partial class It : object, JIt where J : It, new() { }
}
鉴于空值是邪恶的,我们希望我们创建的每种类型都有一个零对象,但空值必须存在以表示尚未到达的数据,这在异步编程中尤其有用。我们使用C#接口的J-前缀来提醒自己,所有接口实例成员都是virtual,就像Java一样,以及C#常量和只读字段的Z-前缀,以注意它们是final,也像Java。枚举器将被用作非常轻量级的阶梯式线程,而不是异步任务和委托,这甚至比纤程更好,因为枚举器的yield比Thread.Yield()便宜得多,大约快10,000多倍,更不用说作为自然循环的简单进度条支持了。这就是空值很重要的原因,因为您总是可以产生空值或特殊对象,例如DBNull.Value发出I/O等待信号。这是以后用的。在本文中,我们要关注缓存中的操作,它通常比内存中的操作快数百倍,而内存中的操作又比磁盘或网络I/O操作快数千或数百万倍,一个I/O层次结构就像一个金字塔,因此我们的新命名空间金字塔。
首先,我们引入空检查:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Is (non- )?
/// Its self-referencing .
/// A .
public static bool Is(this J it) => it != null;
/// Is (non- ) for the ?
/// Its self-referencing .
/// A .
/// An alias .
public static bool Is(this J it, [MaybeNullWhen(false), NotNullWhen(true)] out J alias) =>
(alias = it).Is();
}
}
它看起来很完美,但第二个方法重载不适用于Nullable
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Be (non- ) a
/// to .
/// Its output .
/// A .
public static IEnumerable Be(this IEnumerable? it) => it ?? JIt.J0.Z;
/// Be (non- ) a
/// to .
/// Its output .
/// A .
public static IEnumerator Be(this IEnumerator? it) =>
it ?? JIt.J0.ZTo;
/// Be (non- ) a
/// to .
/// Its output .
/// A .
///
public static IReadOnlyCollection Be(this IReadOnlyCollection? it) =>
it ?? JIt.J0.ZReadOnlyCollection;
/// Be (non- ) a
/// to .
/// Its output .
/// A .
public static IReadOnlyList Be(this IReadOnlyList? it) =>
it ?? JIt.J0.ZReadOnlyList;
/// Be (non- ) a to
/// .
/// Its self-referencing .
/// A .
public static J Be(this J? it) where J : JIt, new() => it ?? JIt.Z0;
/// Be (non- ) a to
/// .
/// Its self-referencing .
/// A .
public static J Be(this J? it) where J : struct => it ?? new();
/// Be (non- ) a
/// to .
/// Its output .
/// A .
public static JOut[] Be(this JOut[]? it) => it ?? JIt.J0.ZArray;
/// Be (non- ) a to
/// .
/// A .
public static string Be(this string? it) => it ?? "";
}
}
就是这个!每当我们调用o.Be()时,如果类型是不可变的并且线程安全为零,我们将最终得到一个共享的默认空类型对象。如果我们愿意遵循所有类型都是不可变的原则,我们甚至可以利用JIt
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Is ( ) ?
/// Its output .
/// An .
public static bool Is(this object? it) => it is JOut;
/// Is ( ) for the ?
/// Its output .
/// An .
/// An alias .
public static bool Is(this object? it, [NotNullWhen(true)] out JOut alias)
where JOut : notnull => it is JOut o ? (alias = o).So(true) : (alias = default!).So(false);
}
}
我们正在使用this object? 通过空检查巧妙地避免调用歧义,知道类型检查总是装箱值类型以获取它们的类型指针。类型检查并没有什么特别之处,它可以被看作是更通用的空检查形式。类型强制更有趣一点,因为as 关键字仅适用于引用类型。我们想概括一下:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Be a to .
/// Its output .
/// An .
[return: NotNullIfNotNull("it")] public static JOut Be(this object? it) => (JOut)it!;
/// Be the to .
/// Its output .
/// An .
/// An alias .
[return: NotNullIfNotNull("it")]
public static JOut Be(this object? it, [NotNullIfNotNull("it")] out JOut alias) =>
alias = it.Be();
}
}
我们选择Be 作为他们的方法名称来将方法组与as 关键字区分开来,该关键字“吞下”了InvalidCastException。 我们不想那样。我们总是希望在发生运行时错误时出现异常。最重要的是,我们希望包括值类型和引用类型,同时允许空值通过类型检查,因为它们应该。为什么不?为什么你可以写return null而不是return (O)null?空值必须通过所有类型检查以确保类型一致性!好吧,事实证明,如果我们让JOut可空,新的C#类型转换正是我们想要的,不再需要as关键字。要使用空检查并行类型检查,我们也非常希望有另一个重载:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Be the to .
/// Its self-referencing .
/// A .
/// An alias .
public static J Be(this J it, out J alias) => alias = it;
}
}
该方法无非是声明一个具有相同值的新变量,事实证明这在单行lambda表达式中非常方便,实际上使所有C#表达式都可以声明内联变量。最后,为了防止引用检查中的意外装箱,我们想添加另一个快捷方法:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Is ?
/// Its self-referencing .
/// Its other .
/// A .
/// A .
public static bool Is(this J it, JThat that)
where J : class? where JThat : class? => ReferenceEquals(it, that);
}
}
通过仅在引用检查中确保引用类型,我们可以在编译时捕获所有意外装箱。无意识的装箱不仅在垃圾收集上缓慢而繁重,而且在产生即使是最有经验的开发人员和专家也可能错过的细微错误方面也很危险。如果可以,请在编译时捕获所有错误!最后,我们包括 补充我们Is 方法的Isnt方法:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// Isn't ( )?
/// Its self-referencing .
/// A .
public static bool Isnt(this J it) => it == null;
/// Isn't ( ) for the ?
/// Its self-referencing .
/// A .
/// An alias .
public static bool Isnt(this J it,
[MaybeNullWhen(true), NotNullWhen(false)] out J alias) => (alias = it).Isnt();
/// Isn't ?
/// Its self-referencing .
/// Its other .
/// A .
/// A .
public static bool Isnt(this J it, JThat that)
where J : class? where JThat : class? => !it.Is(that);
/// Is ( ) not ?
/// Its output .
/// An .
public static bool Isnt(this object? it) => !it.Is();
/// Is ( ) not for the ?
/// Its output .
/// An .
/// An alias .
public static bool Isnt(this object? it, [NotNullWhen(false)] out JOut alias)
where JOut : notnull => !it.Is(out alias);
}
}
这对于现在来说已经足够了,我们将不时重新访问一小部分代码空间,以在全局范围内添加快速和大胜利。所有这些扩展方法都非常小,自动候选JIT内联,在提供代码安全性和质量的同时成本为零。哦,是的, 我们lambda表达式中的那个小So方法是什么?好吧,作为lambda爱好者,我们绝对希望在我们的武器库中有这个快捷方式,它只不过是将表达式链接到一行中:
namespace Pyramid.Kernel.Up
{
partial class It
{
/// So the to .
/// Its self-referencing .
/// Its next .
/// A .
/// A .
[SuppressMessage("", "IDE0060")]
public static JNext So(this J it, JNext next) => next;
}
}
我们的短文到此结束。下一次,我们将讨论“索引”检查,除了空检查和类型检查,还有另一个关于运行时安全的提示和技巧。之后,我们可以进入更有趣的事情。
https://www.codeproject.com/Tips/5305799/How-to-Start-a-Robust-Csharp-Project-in-the-New-Nu