dev383 taking control of the base classes in the.net framework keith pleas architect, guided design...
TRANSCRIPT
DEV383
Taking Control of the Base Classes in the .NET Framework
Keith PleasArchitect, Guided [email protected]
2
Preventing Code Defects
Focus for this talkManaged code
Code that looks correct
Works correctly most of the time
May not work all the time, in all places or with all inputs
Expensive problems
Interludes…
Non FocusSecurity feature usage
Globalization feature usage
3
TakeAways …
Identify specific coding patterns to avoid
Remember to keep an eye out for issues, and guidelines
Test, Test, Test!
4
“I was sure it was June”
demo #1demo #1
Let’s begin with an easy oneLet’s begin with an easy oneControlling your date formats yourself …Controlling your date formats yourself …
5
Example #1 Coding Guideline
Case matters: format specifiers/identifiersBe careful in case-agnostic languages: VB!
Consider using predefined formatsThere are many available, predefined formats. For example, short (d), long (D), full (f), or universal (u)
Consider using a CultureInfo to reset specific formats
Dim ci As New CultureInfo( Thread.CurrentThread.CurrentCulture.ToString())
ci.DateTimeFormat.ShortDatePattern = “MM/dd”
labelDate.Text = DateTime.Now.ToString(“d”, ci)
6
Example #1 Testing Guideline
QuestionWhy should I have to verify date formatting?
Simple problem: simple solutionThis issue can be very easily solved with some quick and easy date range checks
Attempting to parse dates you output can help
Be careful to ensure you consider your current culture when testing: datetime formatting/parsing varies with culture
7
“That wasn’t in the script!”
demo #2demo #2
Simple tasks, simple problems…Simple tasks, simple problems…An ASP.NET “Hello World!” applicationAn ASP.NET “Hello World!” application
8
Example #2 Coding Guideline
All uncontrolled inputs must either be checked or encoded
Input properties like TextBox.TextOther uncontrolled information like URI parameters and cookies
Encoding is best if just echoing content Use HttpUtility.HtmlEncode
Validation of content is better!Also protects against other forms of injection, such as SQL injectionRegularExpressionValidator is very useful
9
Example #2 Testing Guideline
No significant change to general web testing methodology
Test every source of inputForm inputs
URI parameters
More complex forms: cookies, raw request data, etc.
Test injection of client script blocks, SQL statements, C#, VB and JScript
10
“A strange sort of case”
demo #3demo #3
Saving important information…Saving important information…Warning when overwriting in Warning when overwriting in Windows.FormsWindows.Forms
11
Example #3 Problem
The string comparison of “Important.txt” and “important.txt” will not be equal in these cultures
TurkeyTurkey AzerbaijanAzerbaijan
12
Example #3 Explanation
““important” does not equal “IMPORTANT”important” does not equal “IMPORTANT”
13
// this was the line: if (String.Compare(file, tempFile, true) == 0) // this was the line: if (String.Compare(file, tempFile, true) == 0)
if (String.Compare(file, tempFile, true, CultureInfo.InvariantCulture) == 0) if (String.Compare(file, tempFile, true, CultureInfo.InvariantCulture) == 0) {{ if (MessageBox.Show(if (MessageBox.Show( "Are you sure you want to overwrite the existing file?", "Are you sure you want to overwrite the existing file?", "Overwrite File: " + file, MessageBoxButtons.YesNo)"Overwrite File: " + file, MessageBoxButtons.YesNo) != DialogResult.Yes) != DialogResult.Yes) {{ return false;return false; } } else else {{ break;break; }}}}
Example #3 The Fix
14
Example #3 Reasoning
Traditionally programming for non-English alphabets is difficult
The framework makes it easier to create an application targeting a single non-English language, but…
More care is required for components that must work in all cultures
15
Example #3 Scope
Another 9 cultures have multi-character differences within the ASCII range (e.g. Czech, Vietnamese)Another 6 cultures have sorting differences within the ASCII rangeAffected APIs
String.Compare, String.CompareToString.ToUpper, String.ToLower, Char.ToUpper, Char.ToLower
Unaffected APIsString.Equals, ‘==’, String.CompareOrdinal
16
Example #3 Coding Guideline
Comparison should be invariant forComparison to fixed ASCII strings
Persisted data such as XML tag names
String IDs like file names, or registry keys
Comparison should be culture-aware forLocalized text displayed to the user
Most user-entered text
Problem affects sorting as well as casing
17
Example #3 Testing Guideline
Option 1: Support only a fixed set of culturesKnow and test the corner cases for those cultures
Option 2: Test sensitive culturesTurkish or Azeri should be regularly included for automated tests
Ideally, test a culture with multi-character difference like Vietnamese
Include sensitive strings in test data “I” for Turkish
“nG” for Vietnamese
18
Strings vs. StringBuilder
interlude: building stringsinterlude: building strings
Building that house as fast as Building that house as fast as possible …possible …
19
“Thy buffer runneth over”
demo #4demo #4
CheckedMate…CheckedMate…What was that about safe code?What was that about safe code?
20
Example #4: Hang On!
What happened to “no more buffer overruns” ?
This is true only for verifiable managed code
Buffer overruns are still a concern in the following situations
Platform Invokes (P/Invoke)
COM Interop
Unsafe C#
Unverifiable languages like MC++
21
// WARNING: caller must do argument checks// WARNING: caller must do argument checksprivate unsafe static void InternalCopyUnguarded(private unsafe static void InternalCopyUnguarded( byte[ ] src, int srcIndex, byte[ ] src, int srcIndex, byte[ ] dest, int destIndex, byte[ ] dest, int destIndex, int length) {int length) { fixed(byte* srcPointer = src, fixed(byte* srcPointer = src, destPointer = dest) { destPointer = dest) { byte* srcPosition = srcPointer + srcIndex;byte* srcPosition = srcPointer + srcIndex; byte* destPosition = destPointer + destIndex;byte* destPosition = destPointer + destIndex; while (length-- > 0) {while (length-- > 0) { *destPosition++ = *srcPosition++;*destPosition++ = *srcPosition++; }} } } }}
Example #4: The Source
22
public static void MemoryCopy(byte[ ] src, int srcIndex, public static void MemoryCopy(byte[ ] src, int srcIndex, byte[ ] dest, int destIndex, byte[ ] dest, int destIndex, int length) {int length) { if ((length < 0) if ((length < 0) || (srcIndex < 0) || (srcIndex < 0) || (destIndex < 0)|| (destIndex < 0) || (srcIndex + length > src.Length)|| (srcIndex + length > src.Length) || (destIndex + length > dest.Length)) {|| (destIndex + length > dest.Length)) { throw new ArgumentException(throw new ArgumentException( Resources.OneOfYourArgumentsWasBad);Resources.OneOfYourArgumentsWasBad); }} InternalCopyUnguarded(src, srcIndex, InternalCopyUnguarded(src, srcIndex, dest, destIndex, length);dest, destIndex, length);}}
Example #4: The Problem
23
Example #4 The Problem
public static void MemoryCopy(byte[ ] src, int srcIndex, public static void MemoryCopy(byte[ ] src, int srcIndex, byte[ ] dest, int destIndex, byte[ ] dest, int destIndex, int length) {int length) { if ((length < 0) if ((length < 0) || (srcIndex < 0) || (srcIndex < 0) || (destIndex < 0)|| (destIndex < 0) || || (srcIndex + length > src.Length)(srcIndex + length > src.Length) || || (destIndex + length > dest.Length)(destIndex + length > dest.Length)) {) { throw new ArgumentException(throw new ArgumentException( Resources.OneOfYourArgumentsWasBad);Resources.OneOfYourArgumentsWasBad); }} InternalCopyUnguarded(src, srcIndex, InternalCopyUnguarded(src, srcIndex, dest, destIndex, length);dest, destIndex, length);}}
24
Example #4 The Fix
public static void MemoryCopy(byte[ ] src, int srcIndex, public static void MemoryCopy(byte[ ] src, int srcIndex, byte[ ] dest, int destIndex, byte[ ] dest, int destIndex, int length) {int length) { if ((length < 0) if ((length < 0) || (srcIndex < 0) || (srcIndex < 0) || (destIndex < 0)|| (destIndex < 0) || || (src.Length - srcIndex < length)(src.Length - srcIndex < length) || || (dest.Length - destIndex < length)(dest.Length - destIndex < length)) {) { throw new ArgumentException(throw new ArgumentException( Resources.OneOfYourArgumentsWasBad);Resources.OneOfYourArgumentsWasBad); }} InternalCopyUnguarded(src, srcIndex, InternalCopyUnguarded(src, srcIndex, dest, destIndex, length);dest, destIndex, length);}}
25
Example #4 Coding Guideline
Limit usage of “unsafe” codeOnly marginally faster
JIT compiler will eliminate redundant checks
VB does not suffer from this!
C# and MC++ guidelinesUse subtraction of positive integers when checking arguments
Alternatively, use the “checked” operator, particularly if multiplication is involved
26
Example #4 Testing Guideline
Invalid Argument TestingIdentify APIs that are passed through into unsafe code
Test a wide variety and combination of invalid arguments (e.g. 0, 1, -1, 2, -2, Int32.MaxValue, Int32.MinValue, 216, 216 + 1 etc.)
White box testing: look for these patterns in code
27
“Like a broken record”
demo #5demo #5
If at first you don’t succeed…If at first you don’t succeed…An application that keeps tryingAn application that keeps trying
28
Public Sub TryConnect(ByVal timeoutSeconds As Integer)Public Sub TryConnect(ByVal timeoutSeconds As Integer)
Dim start As Date = DateTime.NowDim start As Date = DateTime.Now DoDo TryTry Connect()Connect() ReturnReturn Catch ex As ExceptionCatch ex As Exception LogConnectionFailure(ex)LogConnectionFailure(ex) End TryEnd Try Loop Until DateDiff( DateInterval.Second, start, _Loop Until DateDiff( DateInterval.Second, start, _ DateTime.Now ) > timeoutSecondsDateTime.Now ) > timeoutSeconds
Throw New Exception(Resources.ConnectionTimedOut)Throw New Exception(Resources.ConnectionTimedOut) End SubEnd Sub
Example #5: The Problem
29
Example #5 The Fix Public Sub TryConnect(ByVal timeoutSeconds As Integer)Public Sub TryConnect(ByVal timeoutSeconds As Integer)
Dim start As Date = Dim start As Date = DateTime.UtcNowDateTime.UtcNow ‘ was Now‘ was Now DoDo TryTry Connect()Connect() ReturnReturn Catch ex As ExceptionCatch ex As Exception LogConnectionFailure(ex)LogConnectionFailure(ex) End TryEnd Try Loop Until DateDiff( DateInterval.Second, start, _Loop Until DateDiff( DateInterval.Second, start, _ DateTime.UtcNowDateTime.UtcNow ) > timeoutSeconds ) > timeoutSeconds
Throw New Exception(Resources.ConnectionTimedOut)Throw New Exception(Resources.ConnectionTimedOut) End SubEnd Sub
30
Example #5 Guidelines
Use universal times for all date arithmeticFixes daylight savings time arithmetic
Works across time zones
No loss of context if persisted
If your component must work 24/7Test critical operations at daylight savings time boundaries
Beware: processing timezone interaction information may also be difficult
31
interlude: Do the right thinginterlude: Do the right thing
Designing For The FutureDesigning For The Future
Avoiding traps …Avoiding traps …
32
ICloneable
Has a single Clone method
Not implemented in a consistent mannerShallow Copy: only the references are copied
Deep Copy: objects are exhaustively copied
How can you rely on the ICloneable nature of an object?ICloneable[] GetCopies ( ICloneable[] clones )
Does a copy share state with the original?
33
The Design Guidelines
Contain excellent guidance on what to do, and what NOT to do
In the case of ICloneable, the guideline would state something like …
Do not implement ICloneable
Note: If you need a cloning mechanism, define your own Clone, or Copy methodology, and ensure that you document clearly whether it is a deep or shallow copy. An appropriate pattern is:
public <type> Copy();
Guidelines can be found in the docs, or at:
http://www.gotdotnet.com/team/libraries/
34
“Across the Great Divide”
demo #6demo #6
It’s a topsy-turvy world…It’s a topsy-turvy world…
35
Example #6 Testing Guideline
For a multi-tier applicationTest scenarios of different tiers running on computers in different time zones
Test both positive and negative time zone offsets
For applications with special date and time logic (calendars, schedulers etc.)
Test all the time zones your application could be running in
36
“Sewing up loose threads”
demo #7demo #7
A server component customizing its A server component customizing its behaviorbehavior
37
Private Shared cachedCulture As CultureInfoPrivate Shared cachedCulture As CultureInfo
Public Shared ReadOnly Property ApplicationCulture() _Public Shared ReadOnly Property ApplicationCulture() _ As CultureInfoAs CultureInfo GetGet If cachedCulture Is Nothing ThenIf cachedCulture Is Nothing Then cachedCulture = New CultureInfo("en-NZ")cachedCulture = New CultureInfo("en-NZ") cachedCulture.DateTimeFormat.ShortDatePattern _cachedCulture.DateTimeFormat.ShortDatePattern _ = "yyyy/MM/dd"= "yyyy/MM/dd" End IfEnd If Return cachedCultureReturn cachedCulture End GetEnd Get End PropertyEnd Property
Example #7 The Problem
38
Private Shared cachedCulture As CultureInfoPrivate Shared cachedCulture As CultureInfo Public Shared ReadOnly Property ApplicationCulture() _Public Shared ReadOnly Property ApplicationCulture() _ As CultureInfoAs CultureInfo GetGet If cachedCulture Is Nothing ThenIf cachedCulture Is Nothing Then
cachedCulture = New CultureInfo("en-NZ")cachedCulture = New CultureInfo("en-NZ")
cachedCulture.DateTimeFormat.ShortDatePattern _cachedCulture.DateTimeFormat.ShortDatePattern _ = "yyyy/MM/dd"= "yyyy/MM/dd" End IfEnd If Return cachedCultureReturn cachedCulture End GetEnd Get End PropertyEnd Property
Example #7 Race Condition
Thread 1Thread 1
Thread 2Thread 2
39
Private Shared cachedCulture As CultureInfoPrivate Shared cachedCulture As CultureInfo
Public Shared ReadOnly Property ApplicationCulture() _Public Shared ReadOnly Property ApplicationCulture() _ As CultureInfoAs CultureInfo GetGet If cachedCulture Is Nothing ThenIf cachedCulture Is Nothing Then Dim culture As New CultureInfo("en-NZ")Dim culture As New CultureInfo("en-NZ") culture.DateTimeFormat.ShortDatePattern _culture.DateTimeFormat.ShortDatePattern _ = "yyyy/MM/dd"= "yyyy/MM/dd" cachedCulture = culturecachedCulture = culture End IfEnd If Return cachedCultureReturn cachedCulture End GetEnd Get End PropertyEnd Property
Example #7 The Fix
40
Example #7 Coding Guideline
Design guideline for threadingShared or static methods and properties should be thread safe
Instance methods and properties are generally not thread safe
Coding techniquesUse a temporary variable so you make a single assignment to the shared field
The garbage collector will take care of extra instances created
41
Example #7 Testing Guideline
Race conditions are hard to findReviewing code is generally the most successful
Stress testingDedicate machines to continually run scenarios for extended periods
Targeted testingImmediately spin up multiple threads
Call static members and properties
42
Key Takeaways
Make whole classes of errors go away
Next StepsParticipate in communities: keep an eye out for similar issues
Read .NET Framework Design Guidelineshttp://www.gotdotnet.com/team/libraries/
Use automated tools: FxCop
43
Ask The ExpertsGet Your Questions Answered
13:00 to 15:00 Thursday 3 July
44
Community Resources
Community Resourceshttp://www.microsoft.com/communities/default.mspx
Most Valuable Professional (MVP)http://www.mvp.support.microsoft.com/
NewsgroupsConverse online with Microsoft Newsgroups, including Worldwidehttp://www.microsoft.com/communities/newsgroups/default.mspx
User GroupsMeet and learn with your peershttp://www.microsoft.com/communities/usergroups/default.mspx
45
evaluationsevaluations
46© 2003 Microsoft Corporation. All rights reserved.© 2003 Microsoft Corporation. All rights reserved.
This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.