泛型方法是通过类型参数声明的方法,如下所示:
C#
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
如下示例演示使用类型参数的 int 调用方法的一种方式:
C#
public static void TestSwap()
{
int a = 1;
int b = 2;
Swap<int>(ref a, ref b);
System.Console.WriteLine(a + " " + b);
}
还可省略类型参数,编译器将推断类型参数。 如下 Swap 调用等效于之前的调用:
C#
Swap(ref a, ref b);
类型推理的相同规则适用于静态方法和实例方法。 编译器可基于传入的方法参数推断类型参数;而无法仅根据约束或返回值推断类型参数。 因此,类型推理不适用于不具有参数的方法。 类型推理发生在编译时,之后编译器尝试解析重载的方法签名。 编译器将类型推理逻辑应用于共用同一名称的所有泛型方法。 在重载解决方案步骤中,编译器仅包含在其上类型推理成功的泛型方法。
在泛型类中,非泛型方法可访问类级别类型参数,如下所示:
C#
class SampleClass<T>
{
void Swap(ref T lhs, ref T rhs) { }
}
泛型是.NET work 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性、类型安全性和效率。泛型的定义下面定义了一个普通类和一个泛型类,我们可以明确看到泛型类和普通类最大的区别就是多了一个<T>。所以,这个<T>就标记了,这个类是泛型类。其中这个T,也可以写成A,B,C,D或其他字符。 public class Generic{ public String Name;}public class Generic<T>{ public T Name;}泛型,顾名思义,就是泛指的类型。好比男人,女人,白人,黑人,可以泛称为【人】。但类型只能是一个类型。 那么泛型和类型之间是什么关系呢?其实很简单,泛型在定义的时候,是泛指类型;在使用的时候,就需要被指定,到底使用哪个类型。即,使用时,就不在是泛指类型,而是特定类型。好比,定义时,定义了一个人。但在使用时,必须明确指定,到底是黑人还是白人。泛型的使用泛型类跟普通类的使用方式一样,都需要实例化对象,再由对象来调用内部的属性或方法。下面代码实例化了泛型Generic,实例化时,还指定了该泛型Generic的指定类型为String。所以要给泛型Generic的属性Name赋值,就需要赋值字符串类型的值。 public static void Excute(){ Generic<String> gs = new Generic<String>(); gs.Name = "Kiba518";}下面代码定义了一个Int类型的泛型Generic。 public static void Excute(){ Generic<int> gs = new Generic<int>(); gs.Name = 518;}泛型的默认值泛型的默认值,如下面代码所示。需要使用default(T)来赋值。不管泛型到底是String,int,bool或者是一个Class类型,都可以被自动赋值。 public static void Excute(){ Generic<int> gs = new Generic<int>(); gs.Name = 518; Generic<Task> gsTask = new Generic<Task>(); gsTask.Name = new Task(()=> { Console.WriteLine("Kiba518"); });} public class Generic<T>{ public T Name = default(T);}泛型的约束在泛型类中,有个特别的约束可供我们使用。当我们不显示的声明时,这个约束不存在。但当我们显示的声明的时候,这个约束就会执行。下面,我们来看看这个特别的约束。 public static void Excute(){ Generic<FanXing> gFanXing = new Generic<FanXing>(); Generic< > gFanXing = new Generic< >(); //Generic<string> gs = new Generic<string>(); 这样定义会报错
}public class Generic<T> where T :{ public T Name = default(T);}public class { public string Name { get; set; }}public class FanXing :{ public new string Name { get; set; }}如上面代码所示,【where T : 】就是这个特别的约束。当显示声明这个约束的时候,定义会限制泛型的类型。什么是限制泛型的类型呢?很简单,泛型T,是泛指某一个类型。我们在定义泛型类时,还需显示的指定类型,此时我们显示指定的类型,要受这个限制。这个限制就是指【where T : 】。它的限制是,要求我们指定的类型T必须是 ,或者该类型继承自 ,如FanXing类。泛型的函数在C#中,泛型不仅可以用于类,还可以直接用于函数。具体使用方式如下: public static void Excute(){ GenericFunc gf = new GenericFunc(); gf.FanXingFunc<FanXing>(new FanXing() { Name="Kiba518"});}public class GenericFunc{ public void FanXingFunc<T>(T obj) { Console.WriteLine(obj.GetType()); }}//泛型方法 public T show<T>(T sParameter)
{
return sParameter;
}
很简单,调用泛型函数的时候,指定泛型函数的[指定类型]即可。但是,这里我们发现一个问题,那就是,在泛型函数里,使用泛型对象的时候,我们发现对象都是 类型的。那我们如果想使用泛型对象里的属性和方法时,要怎么办呢?也很简单,反射就可以了。下面我们添加一个反射函数GetPropertyValue,专门用来获取属性。 public class GenericFunc{ public void FanXingFunc<T>(T obj) { var name = GetPropertyValue(obj, "Name"); Console.WriteLine(name); } public GetPropertyValue( obj, string name) { drv1 = obj.GetType().GetProperty(name).GetValue(obj, null); return drv1; }}输出结果如下:这样我们就得到了我们想要的结果,如果想使用泛型类里的函数,道理也一样,只需要用反射来调用即可。结语看到这里,有些同学可能会觉得泛型很复杂,连使用其对象下的属性,都得反射,太繁琐了,还不如不用呢。有这样想法的同学,心里想想就好了,如果对老司机这么说,他肯定会内心默默的微笑,然后对你说,你想的没错。然后,你就没有然后了。泛型的应用,开篇已经说了,主要用在提高代码的可重用性、类型安全性和效率上。如果只是定义一个类,调用一个属性,那泛型的存在就是鸡肋。但事实上,我们的系统永远只有更复杂,更复杂,更复杂。因此泛型才有了用武之地。
1. 使用字典原因
通常情况下,我们可以通过int类型的索引号来从数组或者list集合中查询所需的数据。但是如果情况稍微复杂一点:索引号是非int型数据比如string或其他类型该如何操作呢。这个时候我们就可以使用字典了。
2. 什么是字典
顾名思义,字典是一种让我们可以通过索引号查询到特定数据的数据结构类型。
关键字 DIctionary
3. 用法及注意事项
(1) C#的Dictionary<Tkey,TValue>类在内部维护两个数组来实现该功能。一个keys数组容纳要从其映射的键,另一个values容纳映射到的值。在Dictionary<Tkey,TValue>集合中插入键/值对时,将自动记录哪
个键和哪个值关联,从而允许开发人员快速和简单地获取具有指定键的值。
(2)C#的Dictionary<Tkey,TValue>集合不能包含重复的键。调用Add方法添加键数组中已有的键将抛出异常。但是,如果使用方括号记法(类似给数组元素赋值)来添加键/值对,就不用担心异常——如果键已
经存在,其值就会被新值覆盖。可用ContainKey方法测试Dictionary<Tkey,TValue>集合是否已包含特定的键。
(3)Dictionary<Tkey,TValue>集合内部采用一种稀疏数据结构,在有大量内存可用时才最高效。随着更多元素的插入,Dictionary<Tkey,TValue>集合可能快速消耗大量内存。
(4)用foreach遍历Dictionary<Tkey,TValue>集合返回一个KeyValuePair<Tkey,TValue>。该结构包含数据项的键和值拷贝,可通过Key和Value属性防蚊每个元素。元素是只读的,不能用它们修改Dictionary<Tkey,TValue>集合中的数据。
【AttributeUsage】自定义特性时使用
System.AttributeUsage声明一个Attribute的使用范围与使用原则。

AllowMultiple 和 Inherited 参数是可选的,所以此代码具有相同的效果:

AttributeTarget的值可以参考1。部分可取值如下:

如果 AllowMultiple 参数设置为 true,则返回特性可对单个实体应用多次。
如果 Inherited 设置为 false,则该特性不由从特性化的类派生的类继承。
Attribute.GetCustomAttribute可以获取一个类的Attribute。
[AttributeUsage(AttributeTargets.Class)]
public class VersionAttribute : Attribute
{
public string Name {
get;
set; }
public string Date {
get;
set; }
public string Describtion {
get;
set; }
}
[Version(Name = "hyddd", Date = "2009-07-20", Describtion = "hyddd's class")]
public class MyCode
{
//
}
class Program
{
static void Main(
string[] args)
{
var info =
typeof(MyCode);
var classAttribute = (VersionAttribute)Attribute.GetCustomAttribute(info,
typeof(VersionAttribute));
Console.WriteLine(classAttribute.Name);
Console.WriteLine(classAttribute.Date);
Console.WriteLine(classAttribute.Describtion);
}
}
首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」,具体到底中断还是继续运行,应该由被通知的线程自己处理。具体来说,当对一个线程,调用 interrupt() 时,① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就可以这样做。① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)Thread thread = new Thread(() -> {
while (!Thread.interrupted()) {
// do more work.
}
});
thread.start();
// 一段时间以后
thread.interrupt();
具体到你的问题,Thread.interrupted()清除标志位是为了下次继续检测标志位。如果一个线程被设置中断标志后,选择结束线程那么自然不存在下次的问题,而如果一个线程被设置中断标识后,进行了一些处理后选择继续进行任务,而且这个任务也是需要被中断的,那么当然需要清除标志位了。
interrupted()是Java提供的一种中断机制,要把中断搞清楚,还是得先系统性了解下什么是中断机制。什么是中断?在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断。中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的interrupted方法,该方法也仅仅是将线程对象的中断标识设成true;接着你需要自己写代码不断地检测当前线程的标识位;如果为true,表示别的线程要求这条线程中断,此时究竟该做什么需要你自己写代码实现。每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。中断的相关方法public void interrupt() 将调用者线程的中断状态设为true。public boolean isInterrupted() 判断调用者线程的中断状态。public static boolean interrupted 只能通过Thread.interrupted()调用。 它会做两步操作:返回当前线程的中断状态;将当前线程的中断状态设为false;如何使用中断?要使用中断,首先需要在可能会发生中断的线程中不断监听中断状态,一旦发生中断,就执行相应的中断处理代码。 当需要中断线程时,调用该线程对象的interrupt函数即可。1.设置中断监听Thread t1 = new Thread( new Runnable(){
public void run(){
// 若未发生中断,就正常执行任务
while(!Thread.currentThread.isInterrupted()){
// 正常任务代码……
}
// 中断的处理代码……
doSomething();
}
} ).start();正常的任务代码被封装在while循环中,每次执行完一遍任务代码就检查一下中断状态;一旦发生中断,则跳过while循环,直接执行后面的中断处理代码。2.触发中断t1.interrupt();上述代码执行后会将t1对象的中断状态设为true,此时t1线程的正常任务代码执行完成后,进入下一次while循环前Thread.currentThread.isInterrupted()的结果为true,此时退出循环,执行循环后面的中断处理代码。如何安全地停止线程?stop函数停止线程过于暴力,它会立即停止线程,不给任何资源释放的余地,下面介绍两种安全停止线程的方法。1.循环标记变量自定义一个共享的boolean类型变量,表示当前线程是否需要中断。中断标识volatile boolean interrupted = false;任务执行函数Thread t1 = new Thread( new Runnable(){
public void run(){
while(!interrupted){
// 正常任务代码……
}
// 中断处理代码……
// 可以在这里进行资源的释放等操作……
}
} );中断函数Thread t2 = new Thread( new Runnable(){
public void run(){
interrupted = true;
}
} );2.循环中断状态中断标识 由线程对象提供,无需自己定义。任务执行函数Thread t1 = new Thread( new Runnable(){
public void run(){
while(!Thread.currentThread.isInterrupted()){
// 正常任务代码……
}
// 中断处理代码……
// 可以在这里进行资源的释放等操作……
}
} );中断函数t1.interrupt();上述两种方法本质一样,都是通过循环查看一个共享标记为来判断线程是否需要中断,他们的区别在于:第一种方法的标识位是我们自己设定的,而第二种方法的标识位是Java提供的。除此之外,他们的实现方法是一样的。上述两种方法之所以较为安全,是因为一条线程发出终止信号后,接收线程并不会立即停止,而是将本次循环的任务执行完,再跳出循环停止线程。此外,程序员又可以在跳出循环后添加额外的代码进行收尾工作。如何处理中断?上文都在介绍如何获取中断状态,那么当我们捕获到中断状态后,究竟如何处理呢?Java类库中提供的一些可能会发生阻塞的方法都会抛InterruptedException异常,如:BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep。当你在某一条线程中调用这些方法时,这个方法可能会被阻塞很长时间,你可以在别的线程中调用当前线程对象的interrupt方法触发这些函数抛出InterruptedException异常。当一个函数抛出InterruptedException异常时,表示这个方法阻塞的时间太久了,别人不想等它执行结束了。当你的捕获到一个InterruptedException异常后,亦可以处理它,或者向上抛出。抛出时要注意???:当你捕获到InterruptedException异常后,当前线程的中断状态已经被修改为false(表示线程未被中断);此时你若能够处理中断,则不用理会该值;但如果你继续向上抛InterruptedException异常,你需要再次调用interrupt方法,将当前线程的中断状态设为true。注意:绝对不能“吞掉中断”!即捕获了InterruptedException而不作任何处理。这样违背了中断机制的规则,别人想让你线程中断,然而你自己不处理,也不将中断请求告诉调用者,调用者一直以为没有中断请求。
interrupt() 它基于「一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。」思想,是一个比较温柔的做法,它更类似一个标志位。其实作用不是中断线程,而是「通知线程应该中断了」,具体到底中断还是继续运行,应该由被通知的线程自己处理。
interrupt() 并不能真正的中断线程,这点要谨记。需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就需要这样做:
- 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
- 在调用阻塞方法时正确处理InterruptedException异常。(例如:catch异常后就结束线程。)
首先讲 interrupt() 方法:
- interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;
- 如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;
- 如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;
- 如果线程在Selector上被阻塞,select方法将立即返回;
- 如果非以上情况,将直接标记 interrupt 状态;
注意:interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的线程,不会受 interrupt 中断;
阻塞情况下中断,抛出异常后线程恢复非中断状态,即 interrupted = false
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Task("1"));
t.start();
t.interrupt();
}
static class Task implements Runnable{
String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("thread has been interrupt!");
}
System.out.println("isInterrupted: " + Thread.currentThread().isInterrupted());
System.out.println("task " + name + " is over");
}
}
}
结果:
thread has been interrupt! isInterrupted: false task 1 is over
上述两种隐含的状态恢复操作,是符合常理的,因为线程标记为中断后,用户没有真正中断线程,必然将其恢复为false。理论上Thread.interrupted()调用后,如果已中断,应该执行退出操作,不会重复调用。
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Task("1"));
t.start();
t.interrupt();
}
static class Task implements Runnable{
String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("first :" + Thread.interrupted());
System.out.println("second:" + Thread.interrupted());
System.out.println("task " + name + " is over");
}
}
}
c# 线程执行gridview 绑定
需求:一个web页面 default.aspx 里面有两个控件GridView1,GridView2,通过两个线程分别加载绑定数据。绑定GridView1:void BindCategory() { SqlConnection conn = ReturnSqlconn(); SqlCommand comm = new SqlCommand("select * from category", conn); conn.Open(); SqlDataReader sdr = comm.ExecuteReader(); GridView1.DataSource = sdr; GridView1.DataBind(); }绑定GridView2:void BindNews() { SqlConnection conn = ReturnSqlconn(); SqlCommand comm = new SqlCommand("select * from News", conn); conn.Open(); SqlDataReader sdr = comm.ExecuteReader(); GridView2.DataSource = sdr; GridView2.DataBind(); }加载两个方法,绑定数据:protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { try { Thread categoryThread = new Thread(new ThreadStart(BindCategory)); Thread newsThread = new Thread(new ThreadStart(BindNews)); categoryThread.Start(); newsThread.Start(); categoryThread.Join(); newsThread.Join(); } catch (Exception ex) { Response.Write(ex); } } }实现效果注意:categoryThread.Join();newsThread.Join();这两个函数很关键,否则导致页面绑定失败;Join方法使创建的2个线程与页面加载同步;Join在MSND上的解释是:在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。通过此方法可以实现单个页面的多线程处理以提高效率。
我们现在对委托做一个总结:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。public delegate void GreetingDelegate(string name);
class Program
{
private static void EnglishGreeting(string name)
{
Console.WriteLine("Good Morning, " + name);
}
private static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name);
}
static void Main(string[] args)
{
GreetPeople("Liker", EnglishGreeting);
GreetPeople("李志中", ChineseGreeting);
Console.ReadLine();
}
}
$(function(){
//请求参数
var list = {};
//
$.ajax({
//请求方式
type : "POST",
//请求的媒体类型
contentType: "application/json;charset=UTF-8",
//请求地址
url : "http://127.0.0.1/admin/list/",
//数据,json字符串
data : JSON.stringify(list),
//请求成功
success : function(result) {
console.log(result);
},
//请求失败,包含具体的错误信息
error : function(e){
console.log(e.status);
console.log(e.responseText);
}
});
});
*******************************************************
$("button").click(function(){
$.get("demo_test.asp",function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});
***********************************************
$("button").click(function(){
$.post("demo_test_post.asp",
{
name:"Donald Duck",
city:"Duckburg"
},
function(data,status){
alert("Data: " + data + "\nStatus: " + status);
});
});