Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 基础篇
本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结。
总共分三个部分:
基础篇主要争对C#初学者,巩固C#常用知识点;
中级篇主要争对WPF布局与美化,在减轻代码量的情况做出漂亮的应用;
终极篇为框架应用实战,包含MVVM框架Prism,ORM框架EntityFramework Core,开源数据库Postgresql。
目录
- Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 基础篇
- Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 中级篇
- 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
子句之间,可以包含以下这些可选子句中的一个或多个:where、orderby、join、let,甚至是其他 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.ValueTuple,
C# 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开发总结 之 基础篇的更多相关文章
- Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 中级篇
本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结. 总共分三个部分: 基础篇主要争对C#初学者,巩固C#常用知识点: 中级篇主要争对WPF布局与Mat ...
- 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 少量代码设计一个登录界面 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...
- 简易音乐播放器主界面设计 - .NET CORE(C#) WPF开发
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 简易音乐播放器主界面设计 - .NET CORE(C#) WPF开发 阅读导航 本文背景 代码 ...
- 简化MVVM属性设置和修改 - .NET CORE(C#) WPF开发
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. 简化MVVM属性设置和修改 - .NET CORE(C#) WPF开发 阅读导航 常用类属性设 ...
- .net core 和 WPF 开发升讯威在线客服与营销系统:背景和产品介绍
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf-m.shengxunwei.com ...
- .net core 和 WPF 开发升讯威在线客服与营销系统:系统总体架构
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...
- .net core 和 WPF 开发升讯威在线客服与营销系统:(插曲)一次端口攻击行为的分析与应对
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...
- .net core 和 WPF 开发升讯威在线客服与营销系统:使用 WebSocket 实现访客端通信
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...
- .net core 和 WPF 开发升讯威在线客服与营销系统:使用 TCP协议 实现稳定的客服端
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...
随机推荐
- Kali系统中20个超好用黑客渗透工具,你知道几个?
1. Aircrack-ng Aircrack-ng是用来破解WEP/WAP/WPA 2无线密码最佳的黑客工具之一! 它通过接收网络的数据包来工作,并通过恢复的密码进行分析.它还拥有一个控制台接口.除 ...
- iMX287A多种方法实现流水灯效果
目录 1.流水灯在电子电路中的地位 2.硬件电路分析 3.先点个灯吧 4.shell脚本实现流水灯 5.ANSI C文件操作实现流水灯 6.Linux 系统调用实现流水灯 @ 1.流水灯在电子电路中的 ...
- vue中如何缓存一些页面
在vue中,有时候我们只想缓存页面中的一些组件或页面,这个时候怎么办呢,我们就需要用判断来加载keep-alive. 例如: // router.js { path: "/driving_l ...
- 在windows上极简安装GPU版AI框架(Tensorflow、Pytorch)
在windows上极简安装GPU版AI框架 如果我们想在windows系统上安装GPU版本的AI框架,比如GPU版本的tesnorflow,通常我们会看到类似下面的安装教程 官方版本 安装CUDA 安 ...
- js 面向对象中,定义一个函数的过程
定义一个函数做的两件事:1: 实例化一个Function对象:2: 实例化一个Object对象,并给该函数扩展prototype属性指向这个构造函数 大致过程如图所示: 每一种引用类型(函数,对象,数 ...
- 学习ConcurrentHashMap并发写机制
1. 前言 上篇文章讲了 Unsafe 类中 CAS 的实现,其实是在为这篇文章打基础.不太熟悉的小伙伴请移步Unsafe 中 CAS 的实现.本篇文章主要基于 OpenJDK8 来做源码解析. 2. ...
- TLS/SSL 梳理
数据加密通篇都是为了防止第三方的劫持伪造,保证连接安全, 毫无遮掩的明文传输只有民风淳朴的时候才是安全的. 先是一些基础的内容: 对称加密 最开始为了对数据进行加密,使用的是对称加密算法,即双方协商好 ...
- MySQL记录操作(多表查询)
准备 建表与数据准备 #建表 create table department( id int, name varchar(20) ); create table employee( id int pr ...
- python3.6 单文件爬虫 断点续存 普通版 文件续存方式
# 导入必备的包 # 本文爬取的是顶点小说中的完美世界为列.文中的aa.text,bb.text为自己创建的text文件 import requests from bs4 import Beautif ...
- 页面高度自适应方法(PC、移动端都适用)
有个项目移动端的首页需要自适应. 宽度已经自适应了 , 高度也要自适应 ,先总结一下方法,PC端也适用. $(function(){ var h = window.innerHeight; $(&qu ...