C#调试入门篇
DotNet程序的调试,是DotNet程序员必备的技能之一,开发出稳定的程序、解决程序的疑难杂症都需要很强大的调试能力。DotNet调试有很多方法和技巧。现在本文就介绍一下借助DebugView工具进行调试的方法,以及由DebugView引申出来的知识点。
DebugView
DebugView是一个查看调试信息的非常棒的工具,支持Debug、Release模式编译的程序,甚至支持内核程序,而且能够定制各种过滤条件,让你只看到关心的输出信息,而且可以定制高亮显示的内容等等,非常方便。
捕捉Release模式的Win32程序输出的调试信息,需要选中Capture Global Win32选项:
过滤与高亮功能
可以通过include、exclude设置过滤条件,包含指定字符串的输出信息将会被过滤。还可以通过exclude条件过滤掉对应进程ID的调试信息。多个条件使用“;”分隔,而且支持“*”通配符。
远程调试
DebugView支持远程捕捉调试信息。首先在远程机器上通过如下命令启动DebugView:
DebugView.exe /a /t /g /s
这样,DebugView就会以服务的方式运行,如下图:
然后在本地机器上启动DebugView,并通过Connect连接到远程机器的DebugView,当远程机器中有调试信息输出时,本地就会捕获到,并展示出来:
输出信息到DebugView的几种方式
DebugView的一些功能是不是让你心动了呢。俗话说心动不如行动,但是在行动之前,首先要知道C#如何将调试信息输出到DebugView中。
通过编程输出一些调试信息到DebugView中,一共有三种方式:
- Debug.WriteLine
- Debugger.Log
- Kernal32.dll中的OutputDebugString方法
一、Debug.WriteLine
通过Debug.WriteLine可以将调试信息写入到DebugView中,如下:
Debug.WriteLine("这是调试信息");
效果如下:
不过此方式只能在Debug模式下有效。具体原因下面会详细介绍。
二、Debugger.Log
Debug.WriteLine已经很好用了,唯一的缺点就是在Release模式下无效。那么在Release模式下就可以使用Debugger.Log方法,示例如下:
Debugger.Log(0, null, "这是Debugger.Log输出的调试信息");
三、Kernel32.dll中的OutputDebugString方法
做C++开发的应该知道可以通过OutputDebugString这个API开实现输出调试信息到DebugView中吧。那么C++能做的,C#也能做。可以通过PInvoke的方式引入此方法,这个API属于Kernel32.dll,如下声明:
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern void OutputDebugString(string message);
然后就可以通过调用此方法,将调试信息输出到DebugView中。
DebugView与日志框架比较
可能有人会说,DebugView能做的事情,我用log4Net,NLog等日志框架也能做,为什么要用DebugView呢?
问的好,那么我就根据平时使用DebugView的经验来给你一个用DebugView的理由:
- DebugView使用非常方便。相比于日志框架庞大的体系,DebugView的使用可谓是十分的简单方便。DebugView只有几百K的大小,占用空间几乎可以忽略不计。从官网下载后,直接运行exe,几乎不需要任何配置就可以正常使用。而相比于DebugView,日志框架可以算的上庞然大物。而当你从官网获得log4Net后,需要进行各种繁杂的配置。甚至你要花上几天时间专门学习一下这套框架。由此可以看出DebugView的使用实在是方便的不能再方便。
- DebugView是可视化工具,支持各种过滤和高亮。DebugView可以通过过滤条件来过滤不关心的信息,只显示相关的调试信息。而日志框架输出的是文件等文本信息,这些信息会包含程序运行过程中的所有信息,虽然可以通过配置文件来指定只输出哪一类信息,但是不如DebugView来的方便简单。
- DebugView可以实时监视。DebugView中有“自动滚动”的功能,程序中输出的调试信息,基本上瞬间就会在DebugView中展示出来,当由于大量信息导致DebugView中的文本框满了后,DebugView可以通过自动滚动滚动条,让你随时都可以看到最新的一条信息,达到类似监视的效果。而日志框架由于其写文本的特性,很难达到这种效果,即使能达到,相信也是需要对日志框架相当清楚了解,才能完成这个效果。
这些理由应该足以让你使用DebugView了吧。使用DebugView的理由肯定还不止这些,如果你有更好的理由,还请分享出来。
当然,DebugView与日志框架,每个都有每个的用途。通过DebugView的方式,只适合短暂的调试,而正式发布的网站或者软件,需要一套记录程序长期以来的运行状态的工具,那么就非日志框架莫属了。所以DebugView与日志框架,要在合适的地方,发挥他们最大的功效。
声明
Log4Net等日志框架,功能足够强大,也足够丰富,相信上面说到的DebugView的功能,也可以通过日志框架来实现。但是和DebugView比较起来,会相对复杂一些。所以上面说到的使用DebugView的理由是基于方便性的比较,DebugView有足够的方便性来让你选择使用他。
ConditionalAttribute详解与条件编译
说到调试,那么肯定有开发人员遇到这种情况,开发产品的时候,遇到一些问题,就在代码中加入了大量的调试输出信息,比如通过Console.WriteLine、MessageBox.Show或者通过Ilog.Log记录日志,甚至临时改变逻辑来验证逻辑的正确性等。经过这些调试信息的帮助,终于解决了产品的问题。但此时又遇到了新的问题,产品最终发布的时候,肯定是不能有这些调试信息的,可是已经加了这么多调试信息,难道要全部删除掉吗。这显然不是一个好办法,这么多代码,手一抖,很容易就删除了不相关的代码,造成不可预估的后果。
做过C/C++开发的,可以从各种跨平台的开源库中看到,一堆一堆的#if....#else....#endif,这就是条件编译,这也是C/C++跨平台所依赖的最基本的东西,在Linux系统,编译这段代码,在Windows系统又编译那段代码,最终实现了代码级别的跨平台。
那么C#中有没有类似的功能呢,答案当然是有,而且有两种:
- 通过给方法加上ConditionalAttribute特性
- 使用#if..#else..#endif,来控制代码的编译
ConditionalAttribute特性
下面是ConditionalAttribute的构造函数:
public ConditionalAttribute(
string conditionString
)
构造函数中的参数conditionString,是一个区分大小写的条件编译符号的字符串。
上面提到Debug.WriteLine时,说到这个功能只在Debug模式下才有用,Release下就不起作用了。
我们从MSDN中看一下Debug.WriteLine的说明:
[ConditionalAttribute("DEBUG")]
public static void WriteLine(
string message
)
由此也就明白了Debug.WriteLine只能在Debug模式下使用,而在Release模式下无效的原因了。
条件编译#if..#else..#endif
C/C++中有#if..#else..#endif,C#中也有这些,他们都被称为预处理器。通过预定义的条件编译符号,来控制编译时编译哪部分代码。如下:
public bool HasPermission(string userName)
{
#if DEBUG
//Debug模式下,不需要做权限判断,直接返回true
return true;
#else
//Release模式下,只有sa用户才有权限
if (!string.IsNullOrEmpty(userName) && userName == "sa")
{
return true;
}
else
{
return false;
}
#endif
}
预定义的Debug宏在什么地方
说到条件编译,是不是只有DEBUG 和 RELEASE两种情况呢,如果是这种情况的话,那也就是说DEBUG和RELEASE两种情况是定义好了的,俗话说就是“做死了”,这是作死的节奏啊。不作死就不会死,至少VS在这点上还没有作死。
让我们来一步步揭开DEBUG的面纱。
既然是条件编译,那么就应该和编译选项有关。我们知道C#项目,有一个属性页,可以设置很多编译的选项,如下:
从图中看到,条件编译是用的DEBUG常量,或者称为DEBUG条件编译符号,是在这个编译生成选项中定义的,如果去掉这个定义,那么编译后的HasPermission方法就会根据用户名进行权限检查,程序中通过Debug.WriteLine输出的调试信息也会输出到DebugView中,也就相当于Release模式下的效果。
其实DEBUG常量与Debug、Release模式并无太大的关系,唯一的关系就是,VS生成的项目中,Debug模式下,默认会选中“定义DEBUG常量”,而Release模式下,默认不会选中。也就是说,Debug模式,默认会定义DEBUG常量,而Release不会,如下图:
既然DEBUG常量与Debug模式无本质上的关联,那么为什么说到Debug,就认为DEBUG呢。道理其实很简单,世上本无路,走的人多了,便成了路。本来这个DEBUG常量只是Debug模式下的默认预定义的常量,只是因为大家习惯了,并且对它的这种预定义还比较认可,时间久了,就自然而然认为DEBUG就代表Debug模式。
虽然我们可以通过去掉DEBUG常量,来使条件编译在Debug模式下达到Release模式的效果,但是建议最好不要这样做,因为这就像是大家普遍都认可的一个约定,如果你一反常态,不遵守这个约定,对于程序,编译没有问题,但是后续维护肯定会相当麻烦,所以还请大侠手下留情。
使用自定义的编译常量
DEBUG常量作为一种普遍的约定,最好不要打破。如果有除DEBUG外的条件编译需要,可以使用自定义的编译常量。
自定义编译常量有两种方法:
- 通过编译生成属性页中的条件编译输入框,定义自己的编译常量。
- 在代码中使用#define预处理,来定义编译常量。
我们可以在条件编译的输入框中,定义自己的编译常量,多个常量之间用分号“;”隔开,而且支持中文常量,如下:
当然,我们也可以在代码中通过#define预处理来定义,比如:
#define 缘生梦
#define hbccdf
但是有一点需要注意,define定义常量必须在using 命名空间之前,否则会造成编译错误。
通过VS和Resharper改变颜色来查看哪些代码真正会被编译
引入条件编译后,我们可以通过VS很快知道哪些代码会被编译:
虽然条件编译的代码可以很直观的看出来,但是Conditional修饰的方法就看不出来了,这时就要借助神器Resharper了,如下图:
从图中看出,release模式,由于没有定义DEBUG、缘生梦两个常量,所以,调用Test、Debug.WriteLine方法的地方就会变暗,这样很直观就知道这些代码不会被编译。再次说明一个道理,神器对于开发有很大的帮助作用。
通过反编译查看生成后的代码
上面总是说,有些代码会被编译,有的则不会,那么真正编译后的效果是怎样的,我们不妨使用另外一个比较强大的工具,反编译工具Reflector,来查看一下反编译后的代码:
从图中的反编译后的代码可以看出,满足条件的代码会真正编译到生成的程序集里,而不满足的代码则不会生成到程序集里。
Conditional修饰的方法,会有方法的实现,但是没有方法的调用。不适合在方法里做一些变量的修改。
总结Conditional和条件编译
- Conditional为方法级别的条件编译,只能修饰方法;#if..#else..#endif为行级别的条件编译,可以指定任意代码。
- Conditional只能修饰无返回值的方法,而且不适合在方法中处理一些变量的值。#if..#else..#endif没有任何限制。
- 可以通过条件编译符号输入框,或者代码中的#define定义条件编译常量,这些常量对于Conditional、#if..#else..#endif均有效。
TRACE常量
从上面多幅图中,可以看到,在Debug和Release模式下都会定义一个TRACE常量。
现在我们知道DEBUG常量是用来控制调用Debug.WriteLine的语句是否被编译。那么TRACE常量呢。
相信很多人也用过System.Diagnostics.Trace类,由DEBUG常量与Debug有关,可以想到TRACE常量与Trace有关,我们来看一下MSDN对于Trace的定义。
从MSDN中,看到Trace类的定义以及可以调用的方法与Debug类似,都有WriteLine方法。下面是Trace的WriteLine方法定义:
[ConditionalAttribute("TRACE")]
public static void WriteLine(
string message
)
由此可以知道,TRACE常量是用来控制Trace类中WriteLine等方法是否被编译的作用。
Debug类与Trace类的区别
到现在为止,我们渐渐的了解了Debug类与Trace类,他们都可以通过WriteLine方法输出调试或跟踪信息,从他们的定义和暴露的方法中,可以看出他们非常相似,那么他们有什么区别呢。
有些人会根据使用的效果总结为:Debug类只能在Debug模式下执行,在Release模式下无效果,而Trace类在Debug和Release模式下都可以执行。
确实,在VS中新建一个项目,分别调用Debug.WriteLine和Trace.WriteLine方法,只有Trace.WriteLine输出的信息,在Release模式下有效。这也看似验证了上面的区别。
但这是他们两个真正的区别吗。我们一点一点来分析。
首先看这两个方法的定义:
由图看到,每个方法都通过ConditionalAttribute关联了一个条件编译常量,Debug关联DEBUG常量,Trace关联TRACE常量。再来看一下这两个常量的定义:
从图中看到,TRACE在Debug和Release模式下都会定义,这样在Debug和Release模式下都会执行,而DEBUG只在Debug模式下才会定义,Debug.WriteLine只在Debug模式下执行。从而也验证了上面的结论。
但DEBUG与TRACE只是在默认的情况下的定义。当改变这些定义后,上面的结论就不再正确。
我们来做这样的实验:Debug模式下只定义TRACE常量,Release模式只定义DEBUG常量
然后在Debug和Release模式下分别执行Debug.WriteLine方法和Trace.WriteLine方法,Debug.WriteLine方法只在Release模式下有效,而Trace.WriteLine方法只在Debug模式下有效。上面的结论不再成立。
为了更好的验证一下我们的结论,对他们进行反编译:
由图中的反编译代码看到,除了关联的条件编译常量不同外,内部调用的方法均为TraceInternal.WriteLine,实现完全一样。
那么下面来总结一下Debug与Trace的区别:
- Debug类关联DEBUG常量,Trace类关联TRACE常量。
- 默认情况下,VS创建的项目Debug模式会定义DEBUG与TRACE,而Release模式只定义TRACE,使Debug只在Debug模式有效,而Trace在Debug和Release模式均有效。
- 可以通过修改DEBUG、TRACE的默认定义,来改变Debug、Trace的默认行为。但是建议最好不要这样做,因为改变了这种默认的约定可能会出现意想不到的问题。
Debug、Debugger、Kernel32的联系
每个人大脑的空间都是有限的,零散的知识很容易忘掉,输出调试信息到DebugView中的三种方法,由于关联性不是很强,很容易会忘掉其中的一两种,那么既然实现相同的功能,他们之间有什么关联吗。
这就要从Debug的MSDN文档说起。
我们知道Debug编译的程序,运行的时候可以通过Debug.WriteLine方法输出调试i信息到DebugView,而MSDN中的解释
将后跟行结束符的消息写入 Listeners 集合中的跟踪侦听器。
并没有说是输出到DebugView中,而是写入到Listeners集合中的跟踪侦听器。为了弄明白原理,有必要深入的研究一下。这时就要依赖反编译工具Reflector了。
从整体看上去,Debug类的每一个方法都通过Conditional与DEBUG常量管理,也就是默认情况下,Debug类的所有方法在Debug模式下均不会编译。
我们再来具体看一下Debug.WriteLine方法:
[Conditional("DEBUG"), __DynamicallyInvokable]
public static void WriteLine(string message)
{
TraceInternal.WriteLine(message);
}
再来看一下TraceInternal方法:
public static void WriteLine(string message)
{
foreach (TraceListener listener in Listeners)
{
listener.WriteLine(message);
if (AutoFlush)
{
listener.Flush();
}
}
return;
}
上面的代码是精简后的代码。从代码中看到会调用集合中的每一个listener的WriteLine方法。
那么这些listener的WriteLine又做了什么呢:
public abstract class TraceListener : MarshalByRefObject, IDisposable
{
public abstract void WriteLine(string message);
}
原来TraceListener的WriteLine是抽象类的抽象方法,那么我们得到的listener是具体类的一个抽象,相当于接口,这是微软的一贯做法,再继续下去,就需要知道是哪些具体的TraceListener了。
继续上面的线索,我们是从Listeners属性中获取到的TraceListener,那么就去看Listeners的get实现:
public static TraceListenerCollection Listeners
{
get
{
InitializeSettings();
if (listeners == null)
{
lock (critSec)
{
if (listeners == null)
{
SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
if (systemDiagnosticsSection != null)
{
//从配置文件获取listener,但是由于此处没有设置配置文件,所以先不研究这个地方
listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
}
else
{
//这里new了一个TraceListener的集合
listeners = new TraceListenerCollection();
//这里我们看到了TraceListener的具体实现类DefaultTraceListener
TraceListener listener = new DefaultTraceListener {
IndentLevel = indentLevel,
IndentSize = indentSize
};
listeners.Add(listener);
}
}
}
}
return listeners;
}
}
从代码中,找到了具体的实现类DefaultTraceListener,那么就快点看看他的WriteLine方法吧,有点迫不及待了。
实际上,WriteLine方法会调用内部的Write方法:
private void Write(string message, bool useLogFile)
{
if (base.NeedIndent)
{
//写缩进,实际是空格
this.WriteIndent();
}
if ((message == null) || (message.Length <= 16384))
{
//输出消息
this.internalWrite(message);
}
else
{
//当消息很长时,会通过internalWrite方法将消息多次输出
int startIndex = 0;
while (startIndex < (message.Length - 16384))
{
this.internalWrite(message.Substring(startIndex, 16384));
startIndex += 16384;
}
this.internalWrite(message.Substring(startIndex));
}
if (useLogFile && (this.LogFileName.Length != 0))
{
//输出到日志文件中
this.WriteToLogFile(message, false);
}
}
与输出信息有关的有两个地方,一个是调用internalWrite,另外一个是WriteToLogFile。从WriteToLogFile是有执行条件的。感兴趣的可以研究一下。重点来看一下internalWrite方法。
private void internalWrite(string message)
{
if (Debugger.IsLogging())
{
//调用Debugger.Log方法,这个方法可以输出信息到DebugView中
Debugger.Log(0, null, message);
}
else if (message == null)
{
SafeNativeMethods.OutputDebugString(string.Empty);
}
else
{
//调用Native方法
SafeNativeMethods.OutputDebugString(message);
}
}
internalWrite中调用了两个比较重要的方法。其中Debugger.Log方法是不是很熟悉呢。我们刚刚在上面总结了三种输出调试信息到DebugView的方法,其中就包含了Debugger.Log,而Debug.WriteLine方法中,又会调用到Debugger.Log方法,这样,这两个方法就建立起了联系。
再来看SafeNativeMethods.OutputDebugString,看到这个类的命名,以及对Win32API的了解,就能想到,这肯定是通过PInvoke等方法对Win32API的封装,看其定义:
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern void OutputDebugString(string message);
果然,这就是对kernel32.dll中的OutputDebugString的封装调用,而这又是上面三种方法其中的一种。好了,这样Debug、Debugger、OutputDebugString就全都联系到了一起,他们之间有了联系,是不是就更容易记忆了。
配置TraceListener
从上面对Debug.WriteLine的一步步跟踪分析的过程中,我们看到了对于TraceListener的获取,一种方式是DefaultTraceListener,另外一种就是如下:
SystemDiagnosticsSection systemDiagnosticsSection = DiagnosticsConfiguration.SystemDiagnosticsSection;
if (systemDiagnosticsSection != null)
{
//从配置文件获取listener,但是由于此处没有设置配置文件,所以先不研究这个地方
listeners = systemDiagnosticsSection.Trace.Listeners.GetRuntimeObject();
}
这几行代码就是从配置文件的system.diagnostics中获取TraceListener。
关于配置文件具体的读取和解析过程,本文就不再详细介绍了,感兴趣的朋友可以自行研究,或者等到我的后面博文详细介绍。
那么现在主要说一下如何通过配置文件来设置TraceListener,如下:
<configuration>
<system.diagnostics>
<trace autoflush="true" indentsize="2">
<listeners>
<add name="myListener" type="System.Diagnostics.ConsoleTraceListener" />
</listeners>
</trace>
</system.diagnostics>
</configuration>
同样,有配置文件,那么就可以通过代码来实现同样的功能,如下:
Debug.Listeners.Add(new ConsoleTraceListener());
Debug.AutoFlush = true;
Debug.WriteLine("这是Debug.WriteLine输出的调试信息");
Debug编译与Release编译的区别
Debug与Release的区别,这个问题,相信很多人都会有疑问,也会有很多人有自己的答案,我听到过的答案有这些:
- Release比Debug运行速度快,执行效率高。
- Debug 是调试用的,可以打断点执行。Release是发布用的,不能打断点执行。
- Debug编译的文件大,Release生成的程序集小。
- Debug会生成pdb文件,Release不生成pdb文件。
这些答案看上去好像都对,但是为什么Debug与Release有这么大的区别呢,这就需要深入的思考一下。
在Debug类与Trace类的区别一节中,相信有些朋友已经明白,Debug编译与Release编译的区别其实是编译配置的不同,DEBUG、TRACE常量的定义就是其中不同的地方。那么Debug编译与Release编译还有什么不同呢。针对这个问题,我总结了一下,主要区别有下面几点:
- 常量定义不同
- 优化代码的不同
- 包含的调试信息不同
- 输出路径不同
接下来,我们详细的看一下。
常量定义不同
这个相信大家已经非常清楚了,Debug模式下会定义DEBUG、TRACE常量,而Release模式下只定义TRACE常量,如下图所示
优化代码不同
Debug模式下,默认不会进行代码的优化,而Release模式下,由于默认选中了“优化代码”选项,所以编译器会对生成的代码进行优化,以提高运行速度。
包含的调试信息不同
编译生成的属性页中有一个“高级”按钮,点击后会弹出一个对话框,然后可以对编译生成进行一些设置,如下
Debug与Release的高级选项对比:
从图中看到,Debug模式下默认会生成全部的调试信息,而Release模式下只生成pdb文件。
输出路径不同
这一点详细大家都很清楚了,Debug模式下会输出到Debug目录,Release模式下会输出到Release目录,如图:
Debug编译与Release编译的区别总结
说了这么多,我们再来思考本节开始时说到的关于Debug编译与Release编译的区别的答案,可以得出这样的结论:
- Release比Debug运行速度快,执行效率高。由于Release模式编译会对生成的代码进行优化,所以会使生成的程序集在运行的时候比Debug编译的程序集运行速度快。
- Debug 是调试用的,可以打断点执行。Release是发布用的,不能打断点执行。由于Release模式对代码进行优化,生成的调试信息只包含pdb文件,所以导致调试起来会比较困难。
- Debug编译的文件大,Release生成的程序集小。依然是由于Release模式对生成的代码进行了优化,所以就导致生成的程序集相对来说比较小。
- Debug会生成pdb文件,Release不生成pdb文件。这个要看VS的默认选项,在2012中,Release模式默认会生成pdb文件的,其他版本的VS可能会有不同的情况。
知道这些区别后,我们就可以通过修改编译配置来使Debug程序达到Release的效果,使Release程序达到Debug的效果。当然,还是那句话,虽然可以实现这样的效果,但是建议绝对不要这样做,不然程序维护起来,肯定会遇到很多问题。
总结的重要性
有人说,互联网这么发达,遇到什么问题直接谷歌百度,内事不决问百度,外事不决问谷歌。但是即使你通过网络解决了问题,如果不消化吸收,经过总结变成自己的东西,那么下次还会遇到同样的问题。书到用时方恨少,一回首,已白了少年头。如果不总结,任他虐我千百遍,我却视他如初见,从网络上找到的方法、代码、解决方案,那是别人的知识,只有经过实践去验证,通过总结,消化吸收,才能将这些知识“据为己有”,为我所用。
那么至少从现在做起,把每天学到的知识,遇到的问题,得到的经验,总结下来,相信自己,这是大牛的节奏。
C#调试入门篇的更多相关文章
- .NET高级调试系列-Windbg调试入门篇
Windbg是.NET高级调试领域中不可或缺的一个工具和利器,也是日常我们分析解决问题的必备.准备近期写2篇精华文章,集中给大家分享一下如果通过Windbg进行.NET高级调试. 今天我们来一篇入门的 ...
- spring boot(一):入门篇
构建微服务:Spring boot 入门篇 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...
- 一个App完成入门篇(七)- 完成发现页面
第七章是入门篇的倒数第二篇文章了,明天整个APP将进入收官. 本节教程主要要教会大家使用二维码扫描和用do_WebView组件加在html页面. 导入项目 do_WebView组件 扫描功能 自定义事 ...
- Unity3D大风暴之入门篇(海量教学视频版)
智画互动开发团队 编 ISBN 978-7-121-22242-9 2014年2月出版 定价:79.00元 328页 16开 编辑推荐 长达800分钟的高清教学视频,手把手教会初学者 数个开发案例 ...
- 【N年前的文章脑补:HttpHandler HttpModule入门篇】
HttpHandler HttpModule入门篇 ASP.Net处理Http Request时,使用Pipeline(管道)方式,由各个HttpModule对请求进行处理,然后到达 HttpHand ...
- spring 入门篇
spring 入门篇 相对于Hibernate(冬眠),Spring(春天),具有更多的诗意与希望的感觉,是为了解决传统J2EE开发效率过低.开发商之间不统一.没有真正实现“写一次到处 ...
- PC游戏编程(入门篇)(前言写的很不错)
PC游戏编程(入门篇) 第一章 基石 1. 1 BOSS登场--GAF简介 第二章 2D图形程式初体验 2.l 饮水思源--第一个"游戏"程式 2.2 知其所以然一一2D图形学基础 ...
- 腾讯QQ会员技术团队:人人都可以做深度学习应用:入门篇(下)
四.经典入门demo:识别手写数字(MNIST) 常规的编程入门有"Hello world"程序,而深度学习的入门程序则是MNIST,一个识别28*28像素的图片中的手写数字的程序 ...
- CoreCLR源码探索(七) JIT的工作原理(入门篇)
很多C#的初学者都会有这么一个疑问, .Net程序代码是如何被机器加载执行的? 最简单的解答是, C#会通过编译器(CodeDom, Roslyn)编译成IL代码, 然后CLR(.Net Framew ...
随机推荐
- C# 解析 Json
C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...
- javascript语言精粹摘要
JavaScript中五种基本类型:string,number,boolean,null,undefined.还有一个对象类型object. javascript只有一个数字类型.它在内部被表示为64 ...
- ready与onload区别一
<!DOCTYPE html><html> <head> <title>ready与onload区别一</title> <meta c ...
- Jpanel和container和jframe的区别
Jpanel和container和jframe的区别 (2012-05-23 19:15:11) 转载▼ 标签: 杂谈 分类: room 看到上上面的几张图,container容器是位于最高层. 下面 ...
- Asp.net使用代码修改配置文件的节点值
使用代码修改配置文件的方法: 1.打开配置文件写入的权限 2.先按节点名称长到要修改的节点,然后删除,紧接着将有新值的节点添加回去 3.关闭配置文件写入的权限 修改Appsetting节点的值,修改其 ...
- ASP.NET Core 1.0 部署 HTTPS (.NET Framework 4.5.1)
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- C# 计算字符串在控制台中的显示长度
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- jQuery in action 3rd - Working with properties, attributes, and data
properties properties 是 JavaScript 对象内在的属性,可以进行动态创建,修改等操作. attributes 指的是 DOM 元素标记出来的属性,不是实例对象的属性. 例 ...
- 为什么匿名内部类只能访问final变量【转】
是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final.因为虽然匿名内部类在方法的内部,但实际编译的时候, ...
- Centos7安装完毕后重启提示Initial setup of CentOS Linux 7 (core)的解决方法
问题: CentOS7安装完毕,重新开机启动后显示: Initial setup of CentOS Linux 7 (core) 1) [x] Creat user 2) [!] License i ...