We've all seen the big "macro" features in .NET, this presentation is to give praise to the "Little Wonders" of .NET -- those little items in the framework that make life as a developer that much easier!
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
More Little Wonders of C#/.NET
1. More “Little Wonders” of C#/.Net
James Michael Hare
2012 Visual C# MVP
Application Architect
Scottrade
August 3rd, 2012
http://www.BlackRabbitCoder.net
Twitter: @BlkRabbitCoder
2. Contact Information
Me:
Blog: http://www.BlackRabbitCoder.net
Twitter: @BlkRabbitCoder
Information on Scottrade Careers:
http://jobs.scottrade.com
Twitter: @scottradejobs
3. What are “Little Wonders”?
The .NET Framework is full of “macro-sized” goodness that
can help make our coding lives easier by automating
common tasks.
But, the .NET Framework also has a lot of smaller “micro-
sized” tips and tricks that can improve code.
Many developers know of most of these, but it is often
surprising how many times newer developers don’t.
This presentation picks up where the first “Little Wonders”
presentation leaves off…
4. How do they help?
Basically, by employing these small items at the right
time, you can increase application:
Readability – some of the wonders make code much
more concise and easy to read.
Maintainability – often goes hand and hand with
readability, by removing ambiguity of the code, it is easier
to maintain without introducing errors.
Performance – a few of the little wonders can even help
increase the performance of your code (depending on
usage).
6. Optional Arguments
Optional arguments allow you to specify a default
value to be used if argument isn’t provided.
Default values must be compile-time constants:
Numeric constant expressions
String constant expressions
null
Can be used to replace redundant overloads when
the only purpose is to default an argument.
9. Named Arguments
Defaults are positional, substituted from left to right:
If you want to “skip”, used named arguments:
10. Default Parameters
There are a couple of potential pitfalls to be aware of:
If used across assemblies, can lead to subtle errors.
If a default parameter is defined in one assembly, and used
in another, must make sure both get recompiled if value
changes.
Default parameters are not inherited.
Defaults established in an interface or base-class do not
directly apply (and can be different from) the sub-class.
Default depends on reference type, not the object type.
11. Chained Constructors
Constructors can call each other directly, which can
reduce the need for redundant code in overloads.
You could use optional arguments or initializers to
reign in overloads, but each have their limitations:
Optional arguments must be set to compile-time
constant expressions.
Initializers aren’t suitable for immutable classes,
readonly fields, get-only properties, or items whose
construction may be “heavy”.
14. Generic Constraints
Generics allow you to make very powerful types and
algorithms, but can be a two-edged sword.
An unconstrained generic type parameter makes no
assumptions, giving you the lowest common
denominator of all types.
Supplying a constraint reduces the types that can be
used to realize the generic, but expands the things
that can be done inside the generic.
15. Generic Constraints
For example, how would unconstrained T handle null?
Unconstrained T can be compared to null, but this is
always false for value types (except Nullable<T>).
Unconstrained T cannot be assigned to null, though
can be assigned to default(T).
17. Generic Constraints
Or what if we want to create a new unconstrained T
instance, or access any of T’s members?
Can’t, no way to know if unconstrained T supports
parameter-less construction or other members.
18. Generic Constraints
Supplying a constraint constrains the matching types, but
also expands what you can do with the generic:
where T : struct – T must be a value type, must be first.
Allows T to be used in other value generics (Nullable<T>, etc.).
where T : class – T must be a reference type, must be first.
Allows T to be assigned to null in generic.
where T : U – T must be, inherit, or implement U
Allows T to access any public members of U in generic.
where T : new() – T must have a public parameter-less
constructor, must be last
Allows T to be constructed in generic.
21. Anonymous Types
We can construct and initialize any type on the fly
using object initialization syntax:
22. Anonymous Types
What if we only need a type for a very localized
purpose?
Why create the boilerplate code for a class that will only
be used in a small, localized section?
Most of these classes are simple POCOs, is it worth a full
definition?
If you need to use a type as a key (equality, hashing), it
can get heavy.
Must implement Equals() and GetHashCode() correctly.
25. Anonymous Types
Anonymous types create throw-away types for you:
Creates type based on name, type, order of properties.
Creates suitable Equals() and GetHashCode().
Syntax is like object initialization, just omit type name.
26. Anonymous Types
Now, much simpler…
Don’t need to define UserDay or UserDayTotal
Don’t need to maintain Equals() and GetHashCode().
Doesn’t clutter up project with throw-away types.
27. Anonymous Types
A few things to be aware of:
Type is generated at compile time based on properties’
names, types, order – any deviation is a different type:
Difficult to use outside of defined scope (which
shouldn’t do anyway, defeats purpose).
28. The Enum Class
Enum is not only the base of enums, it also has
several useful static methods:
IsDefined() – Determines if an enum constant exists with
the given value.
HasFlag() – Determines if a given set of bits is set, much
easier to read than the typical bitwise AND.
TryParse() – Parse a string value into enum value, much
lighter than Parse().
29. The Enum Class
Use IsDefined() to see if a value exists
And HasFlag() simplifies flag testing:
30. The Enum Class
The TryParse() is much lighter than Parse(), especially
if you think you may not have a valid value:
31. The Nullable<T> Struct
You can indicate an optional value easily with null for
reference types, but value types always exist.
Sometimes, appropriate sentinel values don’t exist.
The Nullable<T> generic struct simulates optional
values for value types.
Nullable<T> has two key properties:
HasValue – True if Value has been set to a value.
Value – The value if set, throws if not.
32. The Nullable<T> Struct
Use when you have a value type which may or may
not contain a valid value.
For example, the DateTime struct’s default value isn’t
very meaningful, use DateTime? Instead.
33. The Nullable<T> Struct
We then must test to make sure it has a value by
using HasValue property.
Once we know the value exists, access Value to
retrieve it.
34. The Nullable<T> Struct
Nullable<T> allows some syntactical shortcuts:
Nullable<T> can be abreviated T?
Nullable<T> can be assigned to null
Nullable<T> can be compared with null
35. The Nullable<T> Struct
Some things to watch out for:
Nullable<T> doesn’t save space if value not specified,
still stores a Value, it’s just not usable.
Accessing Value when HasValue is not true is an error
and will throw an exception.
Math or logical operations between Nullable<T>
wrapped numeric types (int, double, etc.) has caveats:
Math with null yields null.
Logical ordered comparison with null yields false.
36. The Lazy<T> Class
Some classes may be expensive to create, especially if
they are not always needed, in these cases lazy
instances are often used.
No need to create your own lazy-initialization logic
anymore, Lazy<T> does it all for you.
Can either call default constructor, or a generator.
Various thread-safety modes so you can choose the
level of performance and safety you require.
40. The Tuples
Sometimes, you need to just throw together several
values to treat as a set of values.
Tuples allow you to do this in a generic way.
Similar to anonymous types, yet different:
Both are immutable and have Equals(), GetHashCode()
Anonymous types have named properties, but not type.
Tuples have generic property names, but named type.
Tuple has static factory methods to easy creation.
44. Interlocked
Allows thread-safe, highly performant manipulation
of numeric values.
Much lighter than full locks for simple operations like:
Increment – adds 1 to the interlocked value
Decrement – subtracts 1 from the interlocked value
Exchange – swaps two values
Add – adds a value to the interlocked value
46. Strings of Repeated Char
Have you ever seen someone construct an array of
repeated char like this?
Instead, quickly create a string of a repeated
character using one of string’s constructors:
48. Joining Strings
The string.Join() has a lot of power
Can Join any array or IEnumerable<T>:
Can join a variable argument list, including mixed types:
49. Generic Delegates
One of the oldest patterns in the OO playbook is to
create a class that is 99% complete except for one
anonymous “work” method, which is typically
abstract…
51. Generic Delegates
Inheriting from a class simply to provide a missing
method per use is often overkill and poor coupling.
Can’t easily change behavior at runtime.
Bloats projects with classes that just override.
52. Generic Delegates
We can instead provide behavior through a delegate.
Behavior can be added with no extra subclasses needed.
There is very limited coupling between the delegate and
the class/method using it.
Behavior could be changed out at runtime.
But defining a new delegate each time gets ugly:
Generic delegates can be used for most delegate
needs.
53. The Action Delegate
Action is a generic family of delegates that take up to
16 arguments and return nothing:
Action – takes zero arguments, returns nothing
Action<T> - takes one argument, returns nothing.
Action<T1, T2> - takes two arguments, returns nothing.
…
Action<T1…T16> - takes 16 arguments, returns nothing.
Action cannot be used for ref or out arguments.
54. The Func Delegate
Similar to Action, but Func returns a value:
Func<TResult> – takes zero args, returns a TResult.
Func<T, TResult> - takes one arg, returns a TResult.
Func<T1, T2, TResult> - takes two args, returns a TResult.
…
Func<T1…T16, TResult> - takes 16 args, returns a TResult.
Func cannot be used for ref or out arguments.
Func<T, bool> is generally preferable to Predicate<T>.
58. Enumerable Repeat
Allows you to create a sequence of the same item a
given number of times.
Doesn’t recreate the item each time, takes whatever
the parameter is and creates a sequence.
For example, to create 10 random numbers:
59. Collection Generators
Often times you’ll have a sequence and want to store
in a collection to:
Avoid re-generating the sequence during multiple
iterations.
Convert the sequence from one sequence type to
another.
Pull the sequence into a collection to avoid deferred
execution issues.
60. Collection Generators
There are several LINQ extension methods to
generate different collections from sequences:
ToArray() – stores sequence in a T[].
ToList() – stores sequence in a List<T>.
ToDictionary() – transforms sequence to a
Dictionary<TKey, TValue> given selectors for TKey and
TValue.
ToLookup() – transforms sequence to a Lookup<TKey,
TValue> - similar to dictionary but can have repeated keys.
61. ToArray and ToList
ToArray() and ToList() are useful for:
Remove effects of deferred execution.
Convert a sequence to a particular kind.
62. ToDictionary
ToDictionary() converts a linear sequence to a
Dictionary that maps a single key to a single value.
Keys can only exist once, multiple key instances throw.
Must provide key selector, value selector optional.
63. ToLookup
ToLookup() is similar to ToDictionary(), but it maps a
key to a set of values (essentially a multi-map).
Again must provide key selector, value optional.