本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结。

总共分三个部分:

基础篇主要争对C#初学者,巩固C#常用知识点;

中级篇主要争对WPF布局与美化,在减轻代码量的情况做出漂亮的应用;

终极篇为框架应用实战,包含MVVM框架Prism,ORM框架EntityFramework Core,开源数据库Postgresql。

目录

  1. Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 基础篇
  2. Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 中级篇
  3. Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 终极篇(待续)

前言

此篇为C#常用知识点的实例说明,如果你是多年C#开发者可以跳过此篇或者只关注最后的新特性。

1、OOP之源 类与实例

一切事物皆对象。

类像产品模版,用它可以生产很多产品(简称实例对象)。

类:具有相同属性和行为的对象的抽象集合。实例对象:具备一组可识别的特性与行为的实体。

举个例子:张三、李四。

幻化出类如下:属性为名字,实例就是张三、李四。

public class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
} 张三=new Person("张三")
李四=new Person("李四")


类与属性的修饰符中需要特别关注如下三个:

sealed:密封效果

  • 修饰类时,类将不可以作为基类继承。
  • 修饰属性与行为时,属性与行为在继承类中无法Override与New。
sealed class SealedClass
{
public int x;
public int y;
} // error class SealedTest2:SealedClass
class SealedTest2
{
static void Main()
{
var sc = new SealedClass();
sc.x = ;
sc.y = ;
Console.WriteLine($"x = {sc.x}, y = {sc.y}");
}
}
// Output: x = 110, y = 150 //------------------
class X
{
protected virtual void F() { Console.WriteLine("X.F"); }
protected virtual void F2() { Console.WriteLine("X.F2"); }
} class Y : X
{
sealed protected override void F() { Console.WriteLine("Y.F"); }
protected override void F2() { Console.WriteLine("Y.F2"); }
} class Z : Y
{
// Attempting to override F causes compiler error CS0239.
// protected override void F() { Console.WriteLine("Z.F"); } // Overriding F2 is allowed.
protected override void F2() { Console.WriteLine("Z.F2"); }
}

internal:程序集访问控制

  • 只有在同一程序集的文件中,内部类型或成员才可访问。
  • 可以修饰类与成员,通常用于组件开发。
// Assembly1.cs
// Compile with: /target:library
internal class BaseClass
{
public static int intM = ;
}
// Assembly1_a.cs
// Compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
var myBase = new BaseClass(); // CS0122 错误
}
}

protected:成员访问控制

  • 只能修饰成员,不可以修饰类。
  • 修饰的成员,派生类内部可以直接访问。
class A
{
protected int x = ;
} class B : A
{
static void Main()
{
var a = new A();
var b = new B(); // Error CS1540, because x can only be accessed by
// classes derived from A.
// a.x = 10; // OK, because this class derives from A.
b.x = ;
}
}

2、OOP之三大特性:

1、封装

实例化的每个对象都包含它能进行操作所需要的全部信息。

好处:减少耦合,在类结构变化时创建的对象可以跟随变化;设置访问限制,对外接口清晰明了。

2、继承 (is-a关系)

站在巨人的肩膀上才能飞得更高。

通过继承,除了被继承类的特性之外,可以通过重写、添加和修改创建自己的独有特性。由于父子关系的原因导致类之间强耦合,最好在is-a关系时使用,has-a关系就不行了。

  • New修饰:显式指示成员不应作为基类成员的重写。
  • 抽象方法:abstract 修饰,必须在直接继承自该类的任何非抽象类中重写该方法。 如果派生类本身是抽象的,则它会继承抽象成员而不会实现它们。
  • 虚方法:virtual修饰,派生类可以根据需要重写该方法,也可以直接使用基类的实现。

3、多态

代码使用基类方法,实际运行时调用派生类对象的重写方法。

相当于派生类的对象可以作为基类的对象处理。 在出现此多形性时,该对象的声明类型不再与运行时类型相同。

public class Shape
{
// A few example members
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; } // Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
} public class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
public class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
public class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
} // Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used whereever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
var shapes = new List<Shape>
{
new Rectangle(),
new Triangle(),
new Circle()
}; // Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
shape.Draw();
}
/* Output:
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/

3、接口与抽象类

在设计的时候有时候很难决定用那种。原则有一个:如果不想波及子类的修改就用抽象类。因为接口定义之后,继承的类必须实现此接口。

  • 接口:包含方法、 属性、 事件和索引器,成员隐式都具有公共访问权限,接口只定义不实现成员。一个接口可能从多个基接口继承,类或结构可以实现多个接口。
  • 抽象类:包含抽象方法,虚方法,特有行为,属性,成员的访问权限可控制。sealed不可以使用,且只能继承一个抽象类。

接口实例:

public delegate void StringListEvent(IStringList sender);

public interface IStringList
{
void Add(string s);
int Count { get; }
event StringListEvent Changed;
string this[int index] { get; set; }
}

抽象类实例:

abstract class A
{
public abstract void F();
} abstract class B: A
{
public void G() {}
} class C: B
{
public override void F() {
// actual implementation of F
}
}

4、泛型

在客户端代码声明并初始化这些类或方法之前,这些类或方法会延迟指定一个或多个类型。不会产生运行时转换或装箱操作的成本或风险。

  • 使用泛型类型可以最大限度地重用代码、保护类型安全性以及提高性能。
  • 泛型最常见的用途是创建集合类。
  • 可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
  • 可以对泛型类进行约束以访问特定数据类型的方法。
// Declare the generic class.
public class GenericList<T>
{
public void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int.
GenericList<int> list1 = new GenericList<int>();
list1.Add(); // Declare a list of type string.
GenericList<string> list2 = new GenericList<string>();
list2.Add(""); // Declare a list of type ExampleClass.
GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
list3.Add(new ExampleClass());
}
}

泛型约束:

class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }

where T : notnull 指定类型参数必须是不可为 null 的值类型或不可为 null 的引用类型(C# 8.0)

where T : unmanaged 指定类型参数必须是不可为 null 的非托管类型(C# 7.3).通过 unmanaged 约束,用户能编写可重用例程,从而使用可作为内存块操作的类型。

unsafe public static byte[] ToByteArray<T>(this T argument) where T : unmanaged
{
var size = sizeof(T);
var result = new Byte[size];
Byte* p = (byte*)&argument;
for (var i = ; i < size; i++)
result[i] = *p++;
return result;
}

5、扩展方法

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。Dapper,System.Linq(对System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable<T> 的扩展)就是经典使用。

扩展方法的优先级总是比类型本身中定义的实例方法低,且只能访问公有成员。 只在不得已的情况下才实现扩展方法。

如果确实为给定类型实现了扩展方法,请记住以下几点:

  • 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。

  • 在命名空间级别将扩展方法置于范围中。 例如,如果你在一个名为 Extensions 的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由 using Extensions; 指令置于范围中。

namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}

6、语言集成查询(LINQ)

一系列直接将查询功能集成到 C# 语言的技术统称。 传统上,针对数据的查询都以简单的字符串表示,而没有编译时类型检查或 IntelliSense 支持。 此外,还需要针对每种数据源学习一种不同的查询语言:SQL 数据库、XML 文档、各种 Web 服务等等。 借助 LINQ,查询成为了最高级的语言构造,就像类、方法和事件一样。

简单点就是使用查询表达式可以查询和转换 SQL 数据库、ADO .NET 数据集、XML 文档和流以及 .NET 集合中的数据。超越了限定于集合的扩展方法System.Linq。Entity Framework就是基于这个自动生成查询Sql操作数据。

  • 查询表达式中的变量全都是强类型,尽管在许多情况下,无需显式提供类型,因为编译器可以推断出。 有关详细信息,请参阅 LINQ 查询操作中的类型关系

  • 只有在循环访问查询变量后,才会执行查询(例如,在 foreach 语句中)。 有关详细信息,请参阅 LINQ 查询简介

  • 应用程序始终将源数据视为 IEnumerable<T> 或 IQueryable<T> 集合。

查询表达式:

查询表达式必须以 from 子句开头,且必须以 select 或 group 子句结尾。 在第一个 from 子句与最后一个 select 或 group 子句之间,可以包含以下这些可选子句中的一个或多个:whereorderbyjoinlet,甚至是其他 from 子句。 还可以使用 into 关键字,使 join 或 group 子句的结果可以充当相同查询表达式中的其他查询子句的源。

// percentileQuery is an IEnumerable<IGrouping<int, Country>>
var percentileQuery =
from country in countries
let percentile = (int) country.Population / 10_000_000
group country by percentile into countryGroup
where countryGroup.Key >=
orderby countryGroup.Key
select countryGroup; // grouping is an IGrouping<int, Country>
foreach (var grouping in percentileQuery)
{
Console.WriteLine(grouping.Key);
foreach (var country in grouping)
Console.WriteLine(country.Name + ":" + country.Population);
}

参见内联实例:

class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
} class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
} class Cat : Pet
{ } class Dog : Pet
{ } public static void MultipleJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
Person phyllis = new Person { FirstName = "Phyllis", LastName = "Harris" }; Cat barley = new Cat { Name = "Barley", Owner = terry };
Cat boots = new Cat { Name = "Boots", Owner = terry };
Cat whiskers = new Cat { Name = "Whiskers", Owner = charlotte };
Cat bluemoon = new Cat { Name = "Blue Moon", Owner = rui };
Cat daisy = new Cat { Name = "Daisy", Owner = magnus }; Dog fourwheeldrive = new Dog { Name = "Four Wheel Drive", Owner = phyllis };
Dog duke = new Dog { Name = "Duke", Owner = magnus };
Dog denim = new Dog { Name = "Denim", Owner = terry };
Dog wiley = new Dog { Name = "Wiley", Owner = charlotte };
Dog snoopy = new Dog { Name = "Snoopy", Owner = rui };
Dog snickers = new Dog { Name = "Snickers", Owner = arlene }; // Create three lists.
List<Person> people =
new List<Person> { magnus, terry, charlotte, arlene, rui, phyllis };
List<Cat> cats =
new List<Cat> { barley, boots, whiskers, bluemoon, daisy };
List<Dog> dogs =
new List<Dog> { fourwheeldrive, duke, denim, wiley, snoopy, snickers }; // The first join matches Person and Cat.Owner from the list of people and
// cats, based on a common Person. The second join matches dogs whose names start
// with the same letter as the cats that have the same owner.
var query = from person in people
join cat in cats on person equals cat.Owner
join dog in dogs on
new { Owner = person, Letter = cat.Name.Substring(, ) }
equals new { dog.Owner, Letter = dog.Name.Substring(, ) }
select new { CatName = cat.Name, DogName = dog.Name }; foreach (var obj in query)
{
Console.WriteLine(
$"The cat \"{obj.CatName}\" shares a house, and the first letter of their name, with \"{obj.DogName}\".");
}
} // This code produces the following output:
//
// The cat "Daisy" shares a house, and the first letter of their name, with "Duke".
// The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley".

详细参照:https://docs.microsoft.com/zh-cn/dotnet/csharp/linq/

7、lambda表达式

匿名函数演变而来。匿名函数是一个“内联”语句或表达式,可在需要委托类型的任何地方使用。 可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数。

在 C# 1.0 中,通过使用在代码中其他位置定义的方法显式初始化委托来创建委托的实例。 C# 2.0 引入了匿名方法的概念,作为一种编写可在委托调用中执行的未命名内联语句块的方式。 C# 3.0 引入了 lambda 表达式,这种表达式与匿名方法的概念类似,但更具表现力并且更简练。任何 Lambda 表达式都可以转换为委托类型。 Lambda 表达式可以转换的委托类型由其参数和返回值的类型定义。 如果 lambda 表达式不返回值,则可以将其转换为 Action 委托类型之一;否则,可将其转换为 Func 委托类型之一。

class Test
{
delegate void TestDelegate(string s);
static void M(string s)
{
Console.WriteLine(s);
} static void Main(string[] args)
{
// Original delegate syntax required
// initialization with a named method.
TestDelegate testDelA = new TestDelegate(M); // C# 2.0: A delegate can be initialized with
// inline code, called an "anonymous method." This
// method takes a string as an input parameter.
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); }; // C# 3.0. A delegate can be initialized with
// a lambda expression. The lambda also takes a string
// as an input parameter (x). The type of x is inferred by the compiler.
TestDelegate testDelC = (x) => { Console.WriteLine(x); }; // Invoke the delegates.
testDelA("Hello. My name is M and I write lines.");
testDelB("That's nothing. I'm anonymous and ");
testDelC("I'm a famous author."); // Keep console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Hello. My name is M and I write lines.
That's nothing. I'm anonymous and
I'm a famous author.
Press any key to exit.
*/
Action<string> greet = name =>
{
string greeting = $"Hello {name}!";
Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!
int[] numbers = { , , , , , , , , ,  };
var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);
Console.WriteLine(string.Join(" ", firstSmallNumbers));
// Output:
// 5 4

8、元组

使用之前必须添加 NuGet 包 System.ValueTupleC# 7.0添加的新功能。

元组主要作为返回多个值方法的返回值,简化定义返回值类型的麻烦。与类和结构一样,使用数据结构存储多个元素,但不定义行为。 既可以获得静态类型检查的所有好处,又不需要使用更复杂的 class 或 struct 语法来创作类型。元组还是对 private 或 internal 这样的实用方法最有用。不过公共方法返回具有多个元素的值时,请创建用户定义的类型(class 或 struct 类型)。

public static double StandardDeviation(IEnumerable<double> sequence)
{
var computation = ComputeSumAndSumOfSquares(sequence); var variance = computation.SumOfSquares - computation.Sum * computation.Sum / computation.Count;
return Math.Sqrt(variance / computation.Count);
} private static (int Count, double Sum, double SumOfSquares) ComputeSumAndSumOfSquares(IEnumerable<double> sequence)
{
double sum = ;
double sumOfSquares = ;
int count = ; foreach (var item in sequence)
{
count++;
sum += item;
sumOfSquares += item * item;
} return (count, sum, sumOfSquares);
}

元组析构:

public static void Main()
{
(string city, int population, double area) = QueryCityData("New York City"); // Do something with the data.
}
public static void Main()
{
var (city, population, area) = QueryCityData("New York City"); // Do something with the data.
}
public static void Main()
{
(string city, var population, var area) = QueryCityData("New York City"); // Do something with the data.
}
public static void Main()
{
string city = "Raleigh";
int population = ;
double area = 144.8; (city, population, area) = QueryCityData("New York City"); // Do something with the data.
}

弃元析构:

using System;
using System.Collections.Generic; public class Example
{
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", , ); Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
} private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = , population2 = ;
double area = ; if (name == "New York City")
{
area = 468.48;
if (year1 == )
{
population1 = ;
}
if (year2 == )
{
population2 = ;
}
return (name, area, year1, population1, year2, population2);
} return ("", , , , , );
}
}
// The example displays the following output:
// Population change, 1960 to 2010: 393,149

类型的Deconstruct用户自定义析构:

using System;

public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; } public Person(string fname, string mname, string lname,
string cityName, string stateName)
{
FirstName = fname;
MiddleName = mname;
LastName = lname;
City = cityName;
State = stateName;
} // Return the first and last name.
public void Deconstruct(out string fname, out string lname)
{
fname = FirstName;
lname = LastName;
} public void Deconstruct(out string fname, out string mname, out string lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
} public void Deconstruct(out string fname, out string lname,
out string city, out string state)
{
fname = FirstName;
lname = LastName;
city = City;
state = State;
}
} public class Example
{
public static void Main()
{
var p = new Person("John", "Quincy", "Adams", "Boston", "MA"); // Deconstruct the person object.
var (fName, lName, city, state) = p;
Console.WriteLine($"Hello {fName} {lName} of {city}, {state}!");
}
}
// The example displays the following output:
// Hello John Adams of Boston, MA!

Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 基础篇的更多相关文章

  1. Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 中级篇

    本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结. 总共分三个部分: 基础篇主要争对C#初学者,巩固C#常用知识点: 中级篇主要争对WPF布局与Mat ...

  2. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...

  3. 简易音乐播放器主界面设计 - .NET CORE(C#) WPF开发

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 简易音乐播放器主界面设计 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...

  4. 简化MVVM属性设置和修改 - .NET CORE(C#) WPF开发

    微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 简化MVVM属性设置和修改 - .NET CORE(C#) WPF开发 阅读导航 常用类属性设 ...

  5. .net core 和 WPF 开发升讯威在线客服与营销系统:背景和产品介绍

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf-m.shengxunwei.com ...

  6. .net core 和 WPF 开发升讯威在线客服与营销系统:系统总体架构

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

  7. .net core 和 WPF 开发升讯威在线客服与营销系统:(插曲)一次端口攻击行为的分析与应对

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

  8. .net core 和 WPF 开发升讯威在线客服与营销系统:使用 WebSocket 实现访客端通信

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

  9. .net core 和 WPF 开发升讯威在线客服与营销系统:使用 TCP协议 实现稳定的客服端

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

随机推荐

  1. 使用 GitHub 开源项目申请 IntelliJ License

    一.写在前面 这次要介绍的是通过使用 GitHub 上的开源项目来申请 IntelliJ Pycharm 的正版 License,只需在 GitHub 上准备一个维护超过3个月的开源项目,就能免费使用 ...

  2. JAVA 中的反射(reflact)

    获取反射加载类(获取类的字节码)的3种方式: Class class1=Class.forName("lession_svc.lession_svc.reflact.Person" ...

  3. PHP实现 3des加密解密

    <?php /** * 3des加密 */ class Encrypt{ public function pkcs5_pad($text, $blocksize) { $pad = $block ...

  4. 30分钟学会Objective-C

    注: 本文首发于我的个人博客:https://evilpan.com/2019/04/05/objc-basics/ 请原谅我的标题党.但是如果你有其他语言的学习经验,要学习Objective-C的语 ...

  5. 细说集群技术(Cluster)

    今天本人给大家讲解一些我对集群技术一个理解,如有不对的或者讲的不好的可以多多提出,我会进行相应的更改,先提前感谢提出意见的各位了!!! 集群(Cluster)技术:通过此可以用较低的成本获取较高的性能 ...

  6. 利用wps创建有目录的PDF/word

    为什么要创建: 在阅读一些行业规范或者很长的文件,像是项目管理方案时,非常麻烦,定位需要重新返回目录去.--->所以我想能不能创建一个带目录的PDF,可以点击直接跳转,那就方便多了. 如何创建: ...

  7. C++ const用法,看这一篇就够了!

    本文主要介绍const修饰符在C++中的主要用法,下面会从两个方面进行介绍:类定义中使用const.非类定义中使用const 1. 非类定义中使用const 非类定义中使用const是指:在除了类定义 ...

  8. SpringBoot2 整合ElasticJob框架,定制化管理流程

    本文源码:GitHub·点这里 || GitEE·点这里 一.ElasticJob简介 1.定时任务 在前面的文章中,说过QuartJob这个定时任务,被广泛应用的定时任务标准.但Quartz核心点在 ...

  9. 玩转 .NET Core 3.0:逐浪CMS新版发布,建站更简单、网站更安全

    2019年11月11日,在大家都忙于网上体会“双11 ”的热闹气氛的时候,逐浪CMS开发者团队正在做着新版本发布的最后工作.此次更新是基本于 .NET Core 3.0开发,也是全国首个基于 .NET ...

  10. LinkedHashMap源码解读

    1. 前言 还是从面试中来,到面试中去.面试官在面试 Redis 的时候经常会问到,Redis 的 LRU 是如何实现的?如果让你实现 LRU 算法,你会怎么实现呢?除了用现有的结构 LinkedHa ...