C#基础篇——泛型
前言
在开发编程中,我们经常会遇到功能非常相似的功能模块,只是他们的处理的数据不一样,所以我们会分别采用多个方法来处理不同的数据类型。但是这个时候,我们就会想一个问题,有没有办法实现利用同一个方法来传递不同种类型的参数呢?
这个时候,泛型也就因运而生,专门来解决这个问题的。
泛型是在C#2.0就推出的一个新语法,由框架升级提供的功能。
说明
泛型通过参数化类型实现在同一份代码上操作多种数据类型。例如使用泛型的类型参数T,定义一个类Stack,
可以用Stack、Stack或者Stack实例化它,从而使类Stack可以处理int、string、Person类型数据。这样可以避免运行时类型转换或封箱操作的代价和风险。泛型提醒的是将具体的东西模糊化。
同时使用泛型类型可以最大限度地重用代码、保护类型安全以及提高性能。
可以创建:泛型接口、泛型类、泛型方法、泛型事件和泛型委托
开始
泛型类
泛型类封装不特定于特定数据类型的操作。 泛型类最常见用法是用于链接列表、哈希表、堆栈、队列和树等集合。 无论存储数据的类型如何,添加项和从集合删除项等操作的执行方式基本相同。
static void Main(string[] args)
{
// T是int类型
GenericClass<int> genericInt = new GenericClass<int>();
genericInt._T = 123;
// T是string类型
GenericClass<string> genericString = new GenericClass<string>();
genericString._T = "123";
}
新建一个GenericClass类
/// <summary>
/// 泛型类
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericClass<T>
{
public T _T;
}
泛型方法
泛型方法是通过类型参数声明的方法, 解决用一个方法,满足不同参数类型
static void Main(string[] args)
{
#region 泛型方法
Console.WriteLine("************Generic**************");
int iValue = 123;
string sValue = "456";
DateTime dtValue = DateTime.Now;
object oValue = "MrValue";
GenericMethod.Show<int>(iValue);//需要指定类型参数
//GenericMethod.Show<string>(iValue);//必须吻合
GenericMethod.Show(iValue);//能省略,自动推算
GenericMethod.Show<string>(sValue);
GenericMethod.Show<DateTime>(dtValue);
GenericMethod.Show<object>(oValue);
#endregion
}
新建一个GenericMethod
/// <summary>
/// 泛型方法
/// </summary>
public class GenericMethod
{
/// <summary>
/// 2.0推出的新语法
/// 泛型方法解决用一个方法,满足不同参数类型;做相同的事儿
/// 没有写死参数类型,调用的时候才指定的类型
/// 延迟声明:把参数类型的声明推迟到调用
/// 推迟一切可以推迟的~~ 延迟思想
/// 不是语法糖,而是2.0由框架升级提供的功能
/// 需要编译器支持+JIT支持
/// </summary>
/// <typeparam name="T">T/S 不要用关键字 也不要跟别的类型冲突 </typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());
}
}
泛型接口
为泛型集合类或表示集合中的项的泛型类定义接口通常很有用处。在c#中,通过尖括号“<>”将类型参数括起来,表示泛型。声明泛型接口时,与声明一般接口的唯一区别是增加了一个。一般来说,声明泛型接口与声明非泛型接口遵循相同的规则。
泛型接口定义完成之后,就要定义此接口的子类。定义泛型接口的子类有以下两种方法。
(1)直接在子类后声明泛型。
(2)在子类实现的接口中明确的给出泛型类型。
static void Main(string[] args)
{
#region 泛型接口
CommonInterface commonInterface = new CommonInterface();
commonInterface.GetT("123");
#endregion
}
新建GenericInterface.cs类文件
/// <summary>
/// 泛型类
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericClass<T>
{
public T _T;
}
/// <summary>
/// 泛型接口
/// </summary>
public interface IGenericInterface<T>
{
//泛型类型的返回值
T GetT(T t);
}
/// <summary>
/// 使用泛型的时候必须指定具体类型,
/// 这里的具体类型是int
/// </summary>
public class CommonClass : GenericClass<int>
{
}
/// <summary>
/// 必须指定具体类型
/// </summary>
public class CommonInterface : IGenericInterface<string>
{
public string GetT(string t)
{
return t;
}
}
/// <summary>
/// 子类也是泛型的,继承的时候可以不指定具体类型
/// </summary>
/// <typeparam name="T"></typeparam>
public class CommonClassChild<T> : GenericClass<T>
{
}
泛型委托
泛型委托主要是想讲一下Action和Func两个委托,因为这两个在Linq中是经常见到的。
Action只能委托必须是无返回值的方法
Fun只是委托必须有返回值的方法
不管是不是泛型委托,只要是委托委托那能用Lamdba表达式,因为不管Lamdba表达式还是匿名函数其实都是将函数变量化。
下面简单的来做的demo说下两个的用法,这个会了基本linq会了一半了。
static void Main(string[] args)
{
#region 泛型委托
Action<string> action = s => {
Console.WriteLine(s);
};
action("i3yuan");
Func<int, int, int> func = (int a, int b) => {
return a + b;
};
Console.WriteLine("sum:{0}", func(1,1));
Console.ReadLine();
#endregion
}
上面其实都是将函数做为变量,这也是委托的思想。action是实例化了一个只有一个字符串参数没有返回值得函数变量。func是实例化了一个有两个int类型的参数返回值为int的函数变量。
可以看到通过Lamdba表达式和泛型的结合,算是又方便了开发者们,更加方便实用。
引入委托常用的另一方式
无论是在类定义内还是类定义外,委托可以定义自己的类型参数。引用泛型委托的代码可以指定类型参数来创建一个封闭构造类型,这和实例化泛型类或调用泛型方法一样,如下例所示:
public delegate void MyDelegate<T>(T item);
public void Notify(int i){}
//...
MyDelegate<int> m = new MyDelegate<int>(Notify);
C#2.0版有个新特性称为方法组转换(method group conversion),具体代理和泛型代理类型都可以使用。用方法组转换可以把上面一行写做简化语法:
MyDelegate<int> m = Notify;
在泛型类中定义的委托,可以与类的方法一样地使用泛型类的类型参数。
class Stack<T>
{
T[] items;
int index
//...
public delegate void StackDelegate(T[] items);
}
引用委托的代码必须要指定所在类的类型参数,如下:
Stack<float> s = new Stack<float>();
Stack<float>.StackDelegate myDelegate = StackNotify;
泛型委托在定义基于典型设计模式的事件时特别有用。因为sender[JX2] ,而再也不用与Object相互转换。
public void StackEventHandler<T,U>(T sender, U eventArgs);
class Stack<T>
{
//…
public class StackEventArgs : EventArgs{...}
public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;
protected virtual void OnStackChanged(StackEventArgs a)
{
stackEvent(this, a);
}
}
class MyClass
{
public static void HandleStackChange<T>(Stack<T> stack, StackEventArgs args){...};
}
Stack<double> s = new Stack<double>();
MyClass mc = new MyClass();
s.StackEventHandler += mc.HandleStackChange;
泛型约束
所谓的泛型约束,实际上就是约束的类型T。使T必须遵循一定的规则。比如T必须继承自某个类,或者T必须实现某个接口等等。那么怎么给泛型指定约束?其实也很简单,只需要where关键字,加上约束的条件。
定义一个People类,里面有属性和方法:
public interface ISports
{
void Pingpang();
}
public interface IWork
{
void Work();
}
public class People
{
public int Id { get; set; }
public string Name { get; set; }
public void Hi()
{
Console.WriteLine("Hi");
}
}
public class Chinese : People, ISports, IWork
{
public void Tradition()
{
Console.WriteLine("仁义礼智信,温良恭俭让");
}
public void SayHi()
{
Console.WriteLine("吃了么?");
}
public void Pingpang()
{
Console.WriteLine("打乒乓球...");
}
public void Work()
{
throw new NotImplementedException();
}
}
public class Hubei : Chinese
{
public Hubei(int version)
{ }
public string Changjiang { get; set; }
public void Majiang()
{
Console.WriteLine("打麻将啦。。");
}
}
public class Japanese : ISports
{
public int Id { get; set; }
public string Name { get; set; }
public void Hi()
{
Console.WriteLine("Hi");
}
public void Pingpang()
{
Console.WriteLine("打乒乓球...");
}
}
打印方法
/// <summary>
/// 打印个object值
/// 1 object类型是一切类型的父类
/// 2 通过继承,子类拥有父类的一切属性和行为;任何父类出现的地方,都可以用子类来代替
/// object引用类型 加入传个值类型int 会有装箱拆箱 性能损失
/// 类型不安全
/// </summary>
/// <param name="oParameter"></param>
public static void ShowObject(object oParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Constraint), oParameter.GetType().Name, oParameter);
Console.WriteLine($"{((People)oParameter).Id}_{((People)oParameter).Name}");
}
在main方法中
static void Main(string[] args)
{
#region Constraint 接口约束
Console.WriteLine("************Constraint*****************");
{
People people = new People()
{
Id = 123,
Name = "走自己的路"
};
Chinese chinese = new Chinese()
{
Id = 234,
Name = "晴天"
};
Hubei hubei = new Hubei(123)
{
Id = 345,
Name = "流年"
};
Japanese japanese = new Japanese()
{
Id = 7654,
Name = "i3yuan"//
};
CommonMethod.ShowObject(people);
CommonMethod.ShowObject(chinese);
CommonMethod.ShowObject(hubei);
CommonMethod.ShowObject(japanese);
Console.ReadLine();
}
#endregion
}
泛型约束总共有五种。
约束 | 说明 |
---|---|
T:结构 | 类型参数必须是值类型 |
T:类 | 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。 |
T:new() | 类型参数必须具有无参数的公共构造函数。 当与其他约束一起使用时,new() 约束必须最后指定。 |
T:<基类名> | 类型参数必须是指定的基类或派生自指定的基类。 |
T:<接口名称> | 类型参数必须是指定的接口或实现指定的接口。 可以指定多个接口约束。 约束接口也可以是泛型的。 |
1、基类约束
上面打印的方法约束T类型必须是People类型。
///
/// 基类约束:约束T必须是People类型或者是People的子类
/// 1 可以使用基类的一切属性方法---权利
/// 2 强制保证T一定是People或者People的子类---义务
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter) where T : People
{
Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
tParameter.Hi();
}
注意:
基类约束时,基类不能是密封类,即不能是sealed类。sealed类表示该类不能被继承,在这里用作约束就无任何意义,因为sealed类没有子类。
2、接口约束
/// <summary>
/// 接口约束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : ISports
{
t.Pingpang();
return t;
}
3、引用类型约束 class
引用类型约束保证T一定是引用类型的。
/// 引用类型约束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : class
{
return t;
}
4、值类型约束 struct
值类型约束保证T一定是值类型的。
/// 值类型类型约束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : struct
{
return t;
}
5、无参数构造函数约束 new()
/// new()约束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : new()
{
return t;
}
泛型约束也可以同时约束多个,例如:
/// <summary>
/// 泛型:不同的参数类型都能进来;任何类型都能过来,你知道我是谁?
/// 没有约束,也就没有自由
/// 泛型约束--基类约束(不能是sealed):
/// 1 可以使用基类的一切属性方法---权利
/// 2 强制保证T一定是People或者People的子类---义务
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter)
where T : People, ISports, IWork, new()
{
Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
tParameter.Hi();
tParameter.Pingpang();
tParameter.Work();
}
注意:有多个泛型约束时,new()约束一定是在最后。
泛型的协变和逆变
public class Animal
{
public int Id { get; set; }
}
public class Cat : Animal
{
public string Name { get; set; }
}
static void Main(string[] args)
{
#region 协变和逆变
// 直接声明Animal类
Animal animal = new Animal();
// 直接声明Cat类
Cat cat = new Cat();
// 声明子类对象指向父类
Animal animal2 = new Cat();
// 声明Animal类的集合
List<Animal> listAnimal = new List<Animal>();
// 声明Cat类的集合
List<Cat> listCat = new List<Cat>();
#endregion
}
那么问题来了:下面的一句代码是不是正确的呢?
1 List<Animal> list = new List<Cat>();
可能有人会认为是正确的:因为一只Cat属于Animal,那么一群Cat也应该属于Animal啊。但是实际上这样声明是错误的:因为List和List之间没有父子关系。
这时就可以用到协变和逆变了。
1 // 协变
2 IEnumerable<Animal> List1 = new List<Animal>();
3 IEnumerable<Animal> List2 = new List<Cat>();
F12查看定义:
可以看到,在泛型接口的T前面有一个out关键字修饰,而且T只能是返回值类型,不能作为参数类型,这就是协变。使用了协变以后,左边声明的是基类,右边可以声明基类或者基类的子类。
协变除了可以用在接口上面,也可以用在委托上面:
Func<Animal> func = new Func<Cat>(() => null);
除了使用.NET框架定义好的以为,我们还可以自定义协变,例如:
/// <summary>
/// out 协变 只能是返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListOut<out T>
{
T Get();
}
public class CustomerListOut<T> : ICustomerListOut<T>
{
public T Get()
{
return default(T);
}
}
使用自定义的协变:
// 使用自定义协变
ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();
在来看看逆变。
在泛型接口的T前面有一个In关键字修饰,而且T只能方法参数,不能作为返回值类型,这就是逆变。请看下面的自定义逆变:
/// <summary>
/// 逆变 只能是方法参数
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListIn<in T>
{
void Show(T t);
}
public class CustomerListIn<T> : ICustomerListIn<T>
{
public void Show(T t)
{
}
}
使用自定义逆变:
// 使用自定义逆变
ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();
协变和逆变也可以同时使用,看看下面的例子:
/// <summary>
/// inT 逆变
/// outT 协变
/// </summary>
/// <typeparam name="inT"></typeparam>
/// <typeparam name="outT"></typeparam>
public interface IMyList<in inT, out outT>
{
void Show(inT t);
outT Get();
outT Do(inT t);
}
public class MyList<T1, T2> : IMyList<T1, T2>
{
public void Show(T1 t)
{
Console.WriteLine(t.GetType().Name);
}
public T2 Get()
{
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
public T2 Do(T1 t)
{
Console.WriteLine(t.GetType().Name);
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
}
使用:
IMyList<Cat, Animal> myList1 = new MyList<Cat, Animal>();
IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//协变
IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆变
IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//逆变+协变
有关可变性的注意事项
- 变化只适用于引用类型,因为不能直接从值类型派生其他类型
- 显示变化使用in和out关键字只适用于委托和接口,不适用于类、结构和方法
- 不包括in和out关键字的委托和接口类型参数叫做不变
泛型缓存
在前面我们学习过,类中的静态类型无论实例化多少次,在内存中只会有一个。静态构造函数只会执行一次。在泛型类中,T类型不同,每个不同的T类型,都会产生一个不同的副本,所以会产生不同的静态属性、不同的静态构造函数,请看下面的例子:
public class GenericCache<T>
{
static GenericCache()
{
Console.WriteLine("This is GenericCache 静态构造函数");
_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
}
private static string _TypeTime = "";
public static string GetCache()
{
return _TypeTime;
}
}
public class GenericCacheTest
{
public static void Show()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(GenericCache<int>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<long>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<DateTime>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<string>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
Thread.Sleep(10);
}
}
}
Main()方法里面调用:
static void Main(string[] args)
{
#region 泛型缓存
GenericCacheTest.Show();
#endregion
}
结果:
从上面的截图中可以看出,泛型会为不同的类型都创建一个副本,所以静态构造函数会执行5次。 而且每次静态属性的值都是一样的。利用泛型的这一特性,可以实现缓存。
注意:只能为不同的类型缓存一次。泛型缓存比字典缓存效率高。泛型缓存不能主动释放。
注意
1.泛型代码中的 default 关键字
在泛型类和泛型方法中会出现的一个问题是,如何把缺省值赋给参数化类型,此时无法预先知道以下两点:
T将是值类型还是引用类型
如果T是值类型,那么T将是数值还是结构
对于一个参数化类型T的变量t,仅当T是引用类型时,t = null语句才是合法的; t = 0只对数值的有效,而对结构则不行。这个问题的解决办法是用default关键字,它对引用类型返回空,对值类型的数值型返回零。而对于结构,它将返回结构每个成员,并根据成员是值类型还是引用类型,返回零或空。下面GenericList类的例子显示了如何使用default关键字。
static void Main(string[] args)
{
#region 泛型代码默认关键字default
// 使用非空的整数列表进行测试.
GenericList<int> gll = new GenericList<int>();
gll.AddNode(5);
gll.AddNode(4);
gll.AddNode(3);
int intVal = gll.GetLast();
// 下面一行显示5.
Console.WriteLine(intVal);
// 用一个空的整数列表进行测试.
GenericList<int> gll2 = new GenericList<int>();
intVal = gll2.GetLast();
// 下面一行显示0.
Console.WriteLine(intVal);
// 使用非空字符串列表进行测试.
GenericList<string> gll3 = new GenericList<string>();
gll3.AddNode("five");
gll3.AddNode("four");
string sVal = gll3.GetLast();
// 下面一行显示five.
Console.WriteLine(sVal);
// 使用一个空字符串列表进行测试.
GenericList<string> gll4 = new GenericList<string>();
sVal = gll4.GetLast();
// 下面一行显示一条空白行.
Console.WriteLine(sVal);
#endregion
Console.ReadKey();
}
public class GenericList<T>
{
private class Node
{
// 每个节点都有一个指向列表中的下一个节点的引用.
public Node Next;
// 每个节点都有一个T类型的值.
public T Data;
}
// 这个列表最初是空的.
private Node head = null;
// 在列表开始的时候添加一个节点,用t作为它的数据值.
public void AddNode(T t)
{
Node newNode = new Node();
newNode.Next = head;
newNode.Data = t;
head = newNode;
}
// 下面的方法返回存储在最后一个节点中的数据值列表. 如果列表是空的, 返回类型T的默认值.
public T GetLast()
{
// 临时变量的值作为方法的值返回.
// 下面的声明初始化了临时的温度
// 类型T的默认值. 如果该列表为空返回默认值.
T temp = default(T);
Node current = head;
while (current != null)
{
temp = current.Data;
current = current.Next;
}
return temp;
}
}
2.泛型集合
通常情况下,建议您使用泛型集合,因为这样可以获得类型安全的直接优点而不需要从基集合类型派生并实现类型特定的成员。下面的泛型类型对应于现有的集合类型:
1、List 是对应于 ArrayList 的泛型类。
2、Dictionary 是对应于 Hashtable 的泛型类。
3、Collection 是对应于 CollectionBase 的泛型类。
4、ReadOnlyCollection 是对应于 ReadOnlyCollectionBase 的泛型类。
5、Queue、Stack 和 SortedList 泛型类分别对应于与其同名的非泛型类。
6、LinkedList 是一个通用链接列表,它提供运算复杂度为 O(1) 的插入和移除操作。
7、SortedDictionary 是一个排序的字典,其插入和检索操作的运算复杂度为 O(log n),这使得它成为 SortedList 的十分有用的替代类型。
8、KeyedCollection 是介于列表和字典之间的混合类型,它提供了一种存储包含自己键的对象的方法。
总结
- 作为一个开发人员,当我们程序代码有相同的逻辑,有可能是方法、接口、类或者委托,只是某些参数类型不同,我们希望代码可以通用、复用,甚至是说为了偷懒,也可以说是在不确定类型的情况下,就应该考虑用泛型的思维去实现。
- 在非泛型编程中,虽然所有的东西都可以作为Object传递,但是在传递的过程中免不了要进行类型转换。而类型转换在运行时是不安全的。使用泛型编程将可以减少不必要的类型转换,从而提高安全性。不仅是值类型,引用类型也存在这样的问题,因此有必要的尽量的去使用泛型集合。
- 在非泛型编程中,将简单类型作为Object传递时会引起装箱和拆箱的操作,这两个过程都是具有很大开销的。使用泛型编程就不必进行装箱和拆箱操作了。
参考 文档 《C#图解教程》
注:搜索关注公众号【DotNet技术谷】--回复【C#图解】,可获取 C#图解教程文件
C#基础篇——泛型的更多相关文章
- [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型
引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...
- [转]C++学习–基础篇(书籍推荐及分享)
C++入门 语言技巧,性能优化 底层硬货 STL Boost 设计模式 算法篇 算起来,用C++已经有七八年时间,也有点可以分享的东西: 以下推荐的书籍大多有电子版.对于技术类书籍,电子版并不会带来一 ...
- DDD分层架构之领域实体(基础篇)
DDD分层架构之领域实体(基础篇) 上一篇,我介绍了自己在DDD分层架构方面的一些感想,本文开始介绍领域层的实体,代码主要参考自<领域驱动设计C#2008实现>,另外参考了网上找到的一些示 ...
- 讲讲Linq to SQL映射(基础篇)
讲讲Linq to SQL映射(基础篇) 这篇主要讲Linq to SQL基于属性的映射.即映射数据库,映射表,映射列,映射关系,映射存储过程, 映射函数.然而创建这种映射有三种方法,他们分别是OR ...
- Java面试题之基础篇概览
Java面试题之基础篇概览 1.一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个public的类,且public的类名必须与文件名相一致. 2.Ja ...
- 转:.NET基础篇——反射的奥妙
反射是一个程序集发现及运行的过程,通过反射可以得到*.exe或*.dll等程序集内部的信息.使用反射可以看到一个程序集内部的接口.类.方法.字段.属性.特性等等信息.在System.Reflectio ...
- java学习笔记-基础篇
Java基础篇 1—12 常识 13 this关键字 14参数传递 16 继承 17 访问权限 28—31异常 1—12 常识 1.文件夹以列表展示,显示扩展名,在地址栏显示全路径 2.javac编译 ...
- Prism+MaterialDesign+EntityFramework Core+Postgresql WPF开发总结 之 基础篇
本着每天记录一点成长一点的原则,打算将目前完成的一个WPF项目相关的技术分享出来,供团队学习与总结. 总共分三个部分: 基础篇主要争对C#初学者,巩固C#常用知识点: 中级篇主要争对WPF布局与美化, ...
- C#基础篇——委托
前言 在本章中,主要是借机这个C#基础篇的系列整理过去的学习笔记.归纳总结并更加理解透彻. 在.Net开发中,我们经常会遇到并使用过委托,如果能灵活的掌握并加以使用会使你在编程中游刃有余,然后对于很多 ...
随机推荐
- 1026 Table Tennis (30分) 难度不高 + 逻辑复杂 +细节繁琐
题目 A table tennis club has N tables available to the public. The tables are numbered from 1 to N. Fo ...
- css3及css技巧
左右对齐:
- 使用vue实现购物车功能
页面效果图: html代码: <div class="shop-car" id='car'> <div class="count-custom" ...
- 洛谷 p1605 迷宫问题 详解
题解:dfs搜索 #include <iostream> #include <algorithm> #include <cstring> #include < ...
- 关于Docker的实践
docker 安装 wget -qO- https://get.docker.com/ | sh 镜像 images search pull commit build tag 查看本地镜像:docke ...
- Jmeter基础-HTTP请求
启动Jmeter 打开jmeter/bin文件/jmeter.bat(Windows执行文件)文件,就可以启动jmeter了 1.创建测试计划 启动后默认有一个TestPlan(测试计划),可修改其名 ...
- android自动化
1.环境安装 JDK 1.8 Appium Android_SDK python https://www.cnblogs.com/xiaohanzi/p/10676720.html https://b ...
- 【ubuntu】开机一直“/dev/sda3:clean, XXX files, XXXX blocks”解决方法
由于该电脑是实验室公用跑模型的机子,在解决过程中,发现是 cuda 导致一直进不了系统.原因是装了两个不同版本的cuda,一个9.2,另一个10.0,因为是公用的,目前尚不清楚,怎么同时装上两个的,也 ...
- Python 每日一练(2)
引言 我又双叒叕的来啦,新博客的第二篇文章,这次是继之前公众号上每日一练的第二个,这次是专题实对于文件的一些处理的练习 主要有以下几类: 1.实现英文文章字频统计 2.统一剪裁某一指定目录下的所有图片 ...
- 创建HttpFilter与理解多个Filter代码的执行顺序
1.自定义的HttpFilter,实现Filter接口 HttpFilter package com.aff.filter; import java.io.IOException; import ja ...