Bill's IT Knowledge Base
Outline size (fully expanded): 27,794 lines
- Web Development
- HTML 4.01
- HTML Basics
- HTML Elements & Syntax
- Case sensitivity of element names
- Element names always upper case
- Opening & closing tags
- Empty elements (these do not have end tags)
- Nesting rules
- HTML Attributes & Their Values
- Required vs. optional attributes
- Case sensitivity of attribute names
- Attribute names always lower-case
- Case insensitivity of attribute values (generally)
- Attribute values typically delimited
- Value delimiters: single or double quotation marks
- Boolean attributes
- These do not take any values
- Simply including the attribute implies a value of TRUE
- Omitting the attribute implies a value of FALSE
- HTML Basic Types
- SGML basic types
CDATA
- Essentiall equivalent to trimmed text
- Syntax:
<![CDATA[ - some content - ]]>
ID
andNAME
tokens (must begin with a letter)
- Lengths
- Pixels
- Percentages
MultiLength
- Content types (MIME types)
- Dates & times
LINK
types- 15 types (e.g.,
Stylesheet
,Start
,Next
,Prev
) - Can define additional types via
profile
attribute ofHEAD
element
- 15 types (e.g.,
- SGML basic types
- Basic HTML Document Structure
- Doctype (DTD) declaration (required)
HTML
HEAD
(required)TITLE
(required)- Optional
profile
attribute (value is a URI) META
element withhttp-equiv, content
, &charset
attributes
BODY
(required)
- Optional
HEAD
Element ComponentsMETA
elements- These place meta data within the HTML document
- Paired attributes
name
&content
- Examples (of
name
properties): "author", "keywords", "description", etc. - Paired attributes
http-equiv
&content
- Used in special situations (technically, for http response headers)
- Most typical used to specify defaults for:
- Document character encoding
- Scripting language
- Style sheet language
- Optional
scheme
attribute (to provide additional information about nature ofname
&content
properties)
STYLE
SCRIPT
LINK
- For external style sheets
- For script (i.e., Javascript) files
- For web-author email address
BASE
- Resolves relative references
- Must precede any URI that refers to an external source
OBJECT
(can be placed inHEAD
element only if theOBJECT
does not render any content)
- Comments
- Character References
- HTML Elements & Syntax
- Document Structure - i.e.,
BODY
elements- Element Identifiers
class
(non-unique). Used as/for:- Style sheet selector
- General purpose processing
- To apply multiple classes to a single element:
class="class1 class2 class3"
(etc.) id
(unique). Used as/for:- Style sheet selector
- Target anchor for hypertext links
- Identifying an individual element for a script
- Linking a legend to an
INPUT
in aFORM
- Identifying fields when extracting HTML data for database applications
- General purpose processing
- Grouping Elements
DIV
(block-level grouping)SPAN
(inline gouping)
- Inline Elements (for structured text):
STRONG
EM
CITE
CODE
SAMPLE
KBD
VAR
DFN
ABBR
(andtitle
attribute)ACRONYM
(andtitle
attribute)Q
(includingcite
attribute)SUB
&SUP
(for super- and sub-script)
- Block-level Elements
- Block formatting
PRE
BLOCKQUOTE
(includingcite
attribute)
- Basic structural
H1
,H2
,H3
, etc.DIV
P
ADDRESS
- Contact information for a document or for one of its parts
- Typically placed at beginning or end of a document
- Data
- Lists
- Ordered, i.e.
OL
tag - Un-ordered, i.e.
UL
tag - Definition lists -
DL
,DT
, &DD
elements - Nested lists (
OL
&UL
tags enclosed withinLI
tags)
- Ordered, i.e.
- Tables
- Table identification (optional)
summary
attributeCAPTION
element (when used must immediately followTABLE
tag)
- Grouping rows (optional)
THEAD
,TFOOT
, &TBODY
elements- If
TFOOT
is used it must precedeTBODY
in the markup
- Grouping columns (optional)
span
attribute (i.e., number of columns)COLGROUP
used for structural groupingCOL
used for presentational grouping
- Setting column widths (3 options):
- Fixed width (pixels)
- Percentage
- Proportional (asterisk syntax, e.g.
width="2*"
)
- Table rows (
TR
) &TH
vs.TD
elements (i.e., cells) - Table cells
abbr
attribute (used only for header cells)headers
attribute- Used with data cells to identify governing header cells
- Value: space-separated list of header cells'
id
values
scope
attribute (header cells only) values:"row"
"col"
"rowgroup"
"colgroup"
- Note:
scope
is generally easier to work with thanheaders
axis
attribute- Used for categorizing rows, columns, or cells
- Values: comma-separated list of categories
- Merging table cells
rowspan
&colspan
attributes- Setting value to "0" merges remainder of table section (rows) or column group (columns)
- Table identification (optional)
- Lists
- Block formatting
- Element Identifiers
- Frames
- I have always avoided using frames
- IMHO…
- They are (overly) complex to work with
- They break bookmarks
- I simply don't like that they 'violate' the concept of a (i.e., single) web page
- Other Elements
- URIs
- Components
- Naming scheme (e.g.,
HTTP
) - Machine name (e.g., williamhwhite.biz)
- Path
- Naming scheme (e.g.,
- Syntax
- Use of
#
sign for anchor identifier (a.k.a., fragment identifier) - Absolute references (i.e., a URI which includes both a naming scheme & a machine name)
- Relative references
- URI only includes path (and possibly fragment identifiers)
- Syntax for relative paths
- Root-relative path
- Path begins with a / (slash)
- Slash indicates site's root folder
- Document-relative path(s)
- ../ means go up one level
- This can be "compounded" (e.g., ../../ means go up two levels)
- When the path begins with a sub-directory of the current folder there is no leading slash
- Root-relative path
- Use of
- Components
- Links
- Creating destination anchors (2 options):
- Set
name
attribute withinA
element - Set
id
attribute of any other element
- Set
- Rendered links
A
element withhref
attributeA
elements may only appear within theBODY
element
- Non-rendered links
LINK
Element (may only appear inHEAD
element)LINK
is an empty elementLINK
is used to specify document relationships
- Roles of links (i.e.,
rel
&rev
attributes)rel
&rev
attributes used in conjunction withhref
attributerel
specifies a forward link &rev
a reverse linkrel
&rev
attributes take 15 native link-types as values:"Alternate"
(for alternate versions of a document)"Stylesheet"
"Start", "Next",
&"Prev"
"Contents", "Index",
&"Glossary"
"Chapter", "Section", "Subsection", "Bookmark"
&"Appendix"
"Help"
(i.e., a Help document)"Copyright"
- Additional link-types can be specified in a custom
profile
- Attributes available to both
A
&LINK
elements:href
hreflang
(used to specify the base language of a linked document)type
(used to specifycontent-type
- i.e., media type)charset
rel
&rev
- Creating destination anchors (2 options):
- Objects & Images
IMG
element- Empty element
- Attributes
alt
- provides a text description in the event the image cannot be located or renderedlongdesc
- complementsalt
src
- URI of imageheight
&width
OBJECT
element- Attributes taking URIs as values:
codebase
- base URI forclassid, data
&archive
valuesclassid
- identifies an implementation- If an html document then URI naming scheme is
http:
- Other naming schemes - e.g.
java:
orclsid:
(for ActiveX applets)
- If an html document then URI naming scheme is
data
archive
- space-separated list of URIs
- Other attributes:
codetype
- Specifies content type
- Examples:
"text/html", "image/gif", "video/mpeg",
&"text/css"
standby
- text to display whileOBJECT
loadsdeclare
- Boolean attribute
- When set, makes current
OBJECT
a declaration only - When set, subsequent
OBJECT
must call to invoke declaredOBJECT
- General requirements of
OBJECT
:- Implementation of the object (i.e., URI for application)
- Data to be rendered
- Additional values needed by the object at run-time
- Note: any of these 3 may be implicit or unneeded
- Rendering objects
- Content (i.e., text) of an
OBJECT
element is not rendered unless the object itself cannot be rendered. - Rendered objects should not appear in
HEAD
element - Alternate renderings
- Embed (successive)
OBJECT(s)
within the original object - If necessary, user agent/browser will work from outside in to find implementations it can render
- Embed (successive)
data
attribute does not have to specify a URI - raw data can be provided in-lineOBJECT
can be used to embed one HTML document inside another
- Content (i.e., text) of an
PARAM
element- Used to initialize objects
- Attributes
name
(required)value
(optional) - value of the run-time paramater specified byname
valuetype
- 3 properties:"data"
(default) -value
will be passed to object as a string"ref"
- a URI (if relative, passed to object unresolved)"object"
- theid
of anotherOBJECT
type
- used only ifvaluetype
is set to"ref"
- Objects can include multiple
PARAM
elements PARAM
elements must precede an object's content (i.e., text)
- Declaring and instantiating objects
- Useful when there will be multiple instances of an object
- Presence of
declare
attribute (without any value) identifies a declaration - An object declaration must have an
id
- Declarations must precede any instantiation within the document
- Object declarations, since they are not rendered, can be placed within
HEAD
element - Declared objects can serve as parameters to other objects
- Attributes taking URIs as values:
- Image Maps - i.e.,
MAP
&AREA
elements (client-side maps)- One
MAP
may contain multipleAREAs
- If two or more defined regions overlap, the element that appears earliest in the document takes precedence
- Areas are not rendered, so
alt
attribute is required MAP
attribute(s):name
usemap
- attribute within other elements to link them to a map- Value of
usemap
attribute is URI using target map'sname
AREA
attributes:shape
- values:"default"
- specifies the entire region"rect"
"circle"
"poly"
coords
- define position & shape on screen- Coordinates relative to the top, left corner of the object.
- All values are lengths, comma-separated
- How values are listed:
- For
rect
: left-x, top-y, right-x, bottom-y - For
circle
: center-x, center-y, radius - For
poly
: x1, y1, x2, y2, etc., with final coordinate pair = first coordinate pair
- For
href
(a URI)nohref
- boolean attribute
- Block-level content
- May be used in lieu of, or along with,
AREA
elements - Includes
A
elements specifing the geometric regions of the image map and the link associated with each region. - Content of block-level,
A
elements are rendered.
- May be used in lieu of, or along with,
- One
- Server-side image maps
- Can only be defined for
IMG
orINPUT
elements - For
IMG
elementsIMG
element must be inside anA
element- The image's boolean attribute
ismap
must be set
- For
INPUT
thetype
attribute must be set to"image"
- Can only be defined for
- URIs
- Interactivity (i.e., Forms)
FORM
Element- General
- All (logical) elements must take a
name
attribute, which represents the control name - Best Practice: set
tabindex
attribute for all (logical) elements- Controls tabbing order
- Values do not have to be sequential numbers
- Form elements can be set to either
disabled
orreadonly
- Both attributes are boolean
- Either attribute removes element from tabbing sequence
readonly
used withINPUT
&TEXTAREA
elementsdisabled
used with remaining (logical) elements
- All (logical) elements must take a
- Attributes:
method
("get"
or"post"
)action
(a URI)enctype
- content type (used with the"post"
method)accept
- Comma-separated list of acceptable content types
- Useful in screening out non-conforming file types being submitted
- General
FIELDSET
&LEGEND
ElementsINPUT
&LABEL
Elements- Values for
type
attribute (INPUT
element):"text"
"password"
"checkbox"
"radio"
"file"
"hidden"
"image"
"button"
"submit"
"reset"
- Other
INPUT
attributes:id
size
(i.e., number of characters to display)- for
"text"
or"password"
sets number of characters to display - Otherwise sets width of control (in pixels)
- for
maxlength
(i.e., limit for"text"
or"password"
inputs)name
- Used to identify "posted" data
- With
radio
buttons each button gets samename
- Checkboxes can also share same
name
value
(i.e., default entry)- For
type="file"
value ofvalue
is default file name - For
type="button"
value ofvalue
becomes the button's label
- For
checked
- For
radio
&checkbox
elements - Set value =
"checked"
to pre-select/set a default - Logically, radio buttons must have a pre-selected/default setting
- For
src
- URI for decorative graphic forsubmit
button
LABEL
element- Is Linked to
INPUT
element withfor
attribute - Value of
for
attribute is set equal to value ofinput
element'sid
attribute - More than one
LABEL
can be associated with a given control - Placing a control inside a
LABEL
element provides for "implicit" linking
- Is Linked to
- Values for
- Other Form Controls
SELECT, OPTGROUP
&OPTION
elements (i.e., drop-down boxes)SELECT
attributes:size
- number of rows displayed at once ifSELECT
is a scrolled list box (optional)multiple
- a boolean which allows user to make multiple selections
SELECT
functionalitySELECT
presents user with amenu
SELECT
presents user with individualOPTIONs
OPTIONs
may be grouped withinOPTGROUPs
- Best Practice: pre-select one
OPTION
using the booleanselected
attribute
OPTION
attributes:selected
(boolean)value
- initial value (optional). If not used initial value =OPTION
contentslabel
- used to provide shorter display than contents of the element- Note:
OPTGROUP
element also useslabel
attribute
TEXTAREA
element- Initial value = contents of element
- Attributes:
rows
- number of visible text linescols
- width of display in average character widthsreadonly
- boolean- Note: If
TEXTAREA
set toreadonly
contents are still submitted with the form
BUTTON
element- Attributes
name
- control namevalue
- default valuetype
:"submit"
"reset"
"push"
(no inherent action - used for script implementations)
- Differences from
INPUT
buttons- Can contain content
- User-agents/browsers often render in 3-D
- Attributes
- Submitting
FORM
data- Method
- "Form Data Set" - sequence of
control-name/current-value
pairs get
method appends form data set to URI (separated by?
)post
method include form data set in body of formget
is more appropriate for small form data sets,post
for larger ones
- "Form Data Set" - sequence of
- "Successful Controls"
- Those controls with a valid
control-name
andcurrent-value
- Successful controls have their values submitted
disabled
controls cannot be successful controls- Only
selected
radio and menu (SELECT
) items can be successful - Controls that are hidden because of attribute or style sheets can still be successful
- Those controls with a valid
- Processing form data sets
- User agent/browser compiles form data set from all successful controls
- Form data set encoded per value of form's
enctype
attribute enctype
values:"application/x-www-form-urlencoded"
- Default value
- Used with
get
method
"multipart/form-data"
- Used when sending files, non-ascii data, or binary data
- Used with
post
method
- Method
- Presentation (Briefly...)
- Styling Options:
- Inline declarations - i.e.,
style
attribute- With CSS, value of
style
attribute takes form"name: value"
- Multiple declarations delimited by semi-colon(s)
- With CSS, value of
- Embedded style sheets
- Declared within
STYLE
tags withinHEAD
element HEAD
element may contain multipleSTYLE
elements
- Declared within
- External style sheets (i.e., CSS files)
LINK
element withinHEAD
- For CSS, can also use use
@import
command withinSTYLE
element - May declare (or import) multiple style sheets
- Best Practice: set default stylesheet language in
HEAD
element- Utilize
META
element - Set value of
http-equiv
to"Content-Style-Type"
- Set value of
content
to style sheet language - e.g.,"text/css"
- Utilize
- Best Practice: use external style sheets
- Inline declarations - i.e.,
- Preferred, Alternate & Persistent Style Sheets
- Basics
- Available only with External Style Sheets
- Unless user selects alternates, user agent/browser will use preferred style sheet
- Style sheets can be grouped under a single name
- Style sheets marked as "persistent" must be applied by browser (even if alternate sheets are used)
- Identifying type of style sheets within
LINK
element- Preferred: set
rel
attribute to"stylesheet"
and use thetitle
attribute - Persistent: set
rel
attribute to"stylesheet"
and do not use thetitle
attribute - Alternate: set
rel
attribute to"alternate stylesheet"
and use thetitle
attribute
- Preferred: set
- Preferred style sheet can also be set in
META
element- Set
http-equiv
attribute to"Default-Style"
- Set
content
attribute to the title declared by the preferred style sheet
- Set
- Basics
- Media Types and Style Sheets
media
attribute- Both
LINK
&STYLE
elements can employmedia
attribute - Attribute values:
"screen"
"tty"
"tv"
"projection"
"handheld"
"print"
"braille"
"aural"
"all"
- Linking
BODY
Elements to Style Sheetsclass
attributeid
attribute- Use of
DIV
&SPAN
elements
- See my section on CSS 2.1 for a full outline of my "HTML Presentation" knowledge base.
- Styling Options:
- Scripting (Briefly...)
- Best Practice: set default scripting language in
HEAD
element- Utilize
META
element - Set value of
http-equiv
to"Content-Script-Type"
- Set value of
content
to scripting language - e.g.,"text/javascript"
- Utilize
- Scripts contained within
SCRIPT
tags run when the document loads - Document can contain multiple
SCRIPT
elements SCRIPT
elements can be placed within eitherHEAD
orBODY
SCRIPT
attributessrc
(a URI) - used to invoke an external scriptdefer
(boolean) - used to indicate that script will not generate any document output- Specifying a value (URI) for
src
will override any (script) content
- Intrinsic Events
- Note: Unless otherwise specified, intrinsic events can be specified with all (logical) elements
onload
&onunload
- Triggered when window has finished loading/removed from window
- Used within
BODY
element
onclick
&ondblclick
onmousedown
&onmouseup
onmouseover
&onmouseout
onmousemove
onfocus
&onblur
- used withA, AREA, LABEL, INPUT, SELECT, TEXTAREA,
&BUTTON
elementsonkeypress
(a key is both pressed and relased)onkeydown
&onkeyup
onsubmit
&onreset
(FORM
element only)onselect
- when user selects text within aINPUT
orTEXTAREA
elementonchange
- Activated when a control loses focus and its value has been changed since gaining focus
- Applies to
INPUT, SELECT,
&TEXTAREA
elements
- See my section on JavaScript for a full outline of my "[X]HTML Scripting" knowledge base.
- Best Practice: set default scripting language in
- Bibliography
-
"HTML 4.01 Specification",
D. Raggett, A. Le Hors, I. Jacobs,
Editors. World Wide Web Consortium (W3C) Recommendation,
24 December 1999. This version of the HTML 4.01 Specification is available at
http://www.w3.org/TR/1999/REC-html401-19991224
. The latest version of the HTML 4.01 Specification is always available athttp://www.w3.org/TR/html401/
. - Lloyd, Ian. Build Your Own Web Site The Right Way Using HTML & CSS. United States of America: SitePoint, 2006. Print.
-
"HTML 4.01 Specification",
D. Raggett, A. Le Hors, I. Jacobs,
Editors. World Wide Web Consortium (W3C) Recommendation,
24 December 1999. This version of the HTML 4.01 Specification is available at
- HTML Basics
- XHTML vs. HTML
- XHTML Concepts
- XHTML = a reformulation of HTML as an XML 1.0 application
- XHTML therefore exposes:
- The HTML Document Object model
- The XML Document Object Model - i.e., the DOM
- Defining an XHTML Document
- XML declaration line required if character encoding is neither UTF-8 nor UTF-16
- Best Practice: Always include (i.e., begin with) an XML declaration
- Root element
- Must be
html
- Must contain a declaration for the XHTML namespace
- Specificially:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- Must be
- DOCTYPE declaration
- Must precede root element
- Three options on DOCTYPE
- Strict:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> - Transitional:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - Frameset:
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> - Mr. White's preference: strict
- Strict:
- Combining XHTML with other namespaces
- These are not strictly conforming XHTML 1.0 documents
- One can create well-formed documents, however
- See W3C.Org documentation for how to:
- Include a non-XHTML namespace within an XHTML document
- Embed XHTML mark-up within a more general XML document
- Because XHTML is XML…
- XHTML documents must be well-formed
- Elements must be properly nested
- Non-empty elements must have closing tags
- Case sensitivity
- XML is case-sensitive
- All element & attribute names must be lower case
- Handling elements in XHTML
- Empty elements
- These must self-close - e.g.,
<br/>
or<br />
- Best Practice: With self-closing elements include a blank space before the closing slash
- Note: Some browsers will do unspeakable things to your XHTML if you omit this space
- Creating a "pseudo-empty" element
- Only certain elements can technically be marked-up as empty
- For example,
<p />
is invalid markup - Work-around (using the
p
element):<p> </p>
- These must self-close - e.g.,
style
&script
elements- In XHTML these elements are declared as containing
PCDATA
content. Therefore …- The
<
and&
characters will be interpreted as the start of markup &
and<
will be recognized as entity references (i.e., they will resolve to&
and<
)
- The
- Work-arounds (2):
- Enclose the style or script within
CDATA
markup - Use external scripts or style sheets
- Enclose the style or script within
- Note: attempting to "hide" script and/or stylesheet declarations within
comment
markup (i.e.,<!--
and-->
) will generally lead to tears
- In XHTML these elements are declared as containing
- Element nesting prohibitions
a
elements must not contain othera
elementspre
elements must not containimg, object, big, small, sub
, orsup
elementsbutton
elements must not containinput, select, textarea, label, button, form, fieldset, iframe
, orisindex
elementslabel
elements must not contain otherlabel
elementsform
elements must not contain otherform
elements- Note: These proscriptions apply through all levels of nesting
- Empty elements
- Handling attributes in XHTML
- Attribute values must appear inside quotation marks
- Boolean attributes
- XHTML does not allow attribute minimization
- HTML Example (of attribute minimization):
<INPUT type="checkbox" checked>
- In XHTML must write as:
<input type="checkbox" checked="checked">
- White-space in atrribute values
- Any & all leading and trailing white space is stripped
- Inter-word sequences of 1 or more white-space characters - including line-breaks - are reduced to a single white-space character
- As a rule, including line-breaks and/or multiple white-space characters in attribute values will lead to tears
name
attribute- In HTML
name
attribute was allowed ina, applet, form, frame, iframe, img
, andmap
elements - In XHTML
name
attribute has been deprecated. Must useid
atrribute instead
- In HTML
- Attrributes with enumerated values (e.g., the
type
attribute of theinput
element)- HTML is case-insensitive about values
- In XHTML values must be lower-case
lang
attribute- Best Practice: when used, also inclue the
xml:lang
attribute - The
xml:lang
attribute takes precedence
- Best Practice: when used, also inclue the
- XHTML documents must be well-formed
- Miscellaneous Issues
- In XHTML when employing hexadecimal values as character entity references must use all lower case characters - i.e.,
&#xnn;
instead of&#Xnn;
- Internet Media Type Declarations - 2 Options:
"text/html"
"application/xhtml+xml"
- Note: Use of
"application/xhtml+xml"
media type explained in this whitepaper
- Fragment identifiers & converting HTML documents to XHTML
- URI references often end with a fragment of the form
"#foo"
- Issues
- Full explanation is found at this W3.org documentation
- Imho W3.org's guidance here is written very poorly
- URI references often end with a fragment of the form
- Character encoding
- HTML: set one of two ways:
- Via the
charset
parameter of the HTTP Content-Type header - Via a
META
element in the document itself
- Via the
- XML: set inside the declaration - e.g.,
<?xml version="1.0" encoding="EUC-JP"?>
- XHTML:
- Preference is to set via the charset parameter of the HTTP Content-Type header
- Where this is not possible
- Use both the:
charset
declaration inside the xml declaration statementhttp-equiv
attribute inside ameta
statement - e.g.,
<meta http-equiv="Content-type" content="text/html; charset=EUC-JP" />
- Where there is a conflict the encoding declaration of the XML declaration prevails
- Use both the:
- HTML: set one of two ways:
- CSS style sheets for XHTML
- Should always use lower case for element & attribute selectors
- Special "handling" is required when XTML is explicitly declared as XML
- XML uses a stylesheet declaration to declare stylesheet rules
- W3.org provides an example of how to mark this all up
- Tables
- An HTML parser infers the existence of a
tbody
element; an XML parser does not - In XHTML, then, must explicitly include
tbody
element if there will be a corresponding CSS selector
- An HTML parser infers the existence of a
- In XHTML when employing hexadecimal values as character entity references must use all lower case characters - i.e.,
- Bibliography
-
"XHTML™ 1.0 The Extensible HyperText Markup Language (Second Edition)",
W3C HTML Working Group,
World Wide Web Consortium (W3C) Recommendation,
26 January 2000,
revised 1 August 2002. This (revised) version of XHTML 1.0 is available at
http://www.w3.org/TR/2002/REC-xhtml1-20020801
. W3C's latest version of XHTML 1.0 is always available athttp://www.w3.org/TR/xhtml1
.
-
"XHTML™ 1.0 The Extensible HyperText Markup Language (Second Edition)",
W3C HTML Working Group,
World Wide Web Consortium (W3C) Recommendation,
26 January 2000,
revised 1 August 2002. This (revised) version of XHTML 1.0 is available at
- XHTML Concepts
- XHTML 1.0 - Strict (draft; incomplete)
- XHTML Basics
- XHTML Concepts
- XHTML represents a reformulation of HTML as an XML application
- XHTML exposes both the HTML Document Object model and the XML Document Object Model - i.e., the DOM
- XHTML Elements & Syntax
- Opening & Closing Tags
- Tag Name (in lower case!)
- Self-Closing Elements
- Attributes (required and/or optional) & Their Values (in quotation marks)
- Nesting Rules
- Required Elements
- Doctype
- Tags
html
head
title
meta: http-equiv, content & charset
body
- Optional
head
Element Componentsmeta
Elements with "name"/"content" pairs, e.g. "author", "description", etc.style
script
link
- For external style sheets
- For script (i.e., Javascript) files
- For web-author email address
base
- Comments
- Entities
- XHTML Concepts
- Document Structure - i.e.,
body
elements- Block-level Elements
- Basic Structural
h1
,h2
,h3
, etc.div
p
- Data
- Lists
- Ordered vs.un-ordered
- Definition lists -
dl
,dt
, &dd
elements
- Tables
caption
elementthead
,tfoot
, &tbody
elements (all optional)- Table rows (
tr
), &th
vs.td
elements (i.e., cells) colgroup
&col
elements- Merging table cells:
rowspan
&colspan
attributes
- Lists
- Block formatting
pre
blockquote
(includingcite
attribute)
- Basic Structural
- Inline Elements (for structured text):
strong
em
span
cite
code
abbr
acronym
q
(for inline quotations)sub
&sup
(for super- and sub-script)
- Block-level Elements
- Other Elements
- Linking & URIs - e.g.,
a
(anchor),link
,script
,img
tagshref
,src
, &cite
attributes- Absolute references
- Relative references (and syntax)
- Fragment identifiers (in XML/XHTML
id
attribute only!)
- Rendered & Non-Rendered Elements
- Enclosed/highlighted Text within
a
Tags - URIs (non-rendered) Within:
- Various
meta
tags blockquote
tagsrel
&rev
attributes within alink
element
- Various
- Within
title
block:rel="prev"
,rel="index"
&rel="next"
attributes within alink
element- Note use of
base
element, cited above
img
element.scr
&alt
attributes.address
Element
- Linking & URIs - e.g.,
- Interactivity (i.e., Forms)
form
Elementmethod
Attribute (get
&post
)action
Attribute
fieldset
&legend
Elementsinput
Element- Values for
type
Attribute:text
password
checkbox
radio
hidden
submit
- Other Attributes:
id
size
(i.e., number of characters to display)maxlength
(i.e., limit on user input)name
- Used to identify "posted" data
- With
radio
buttons each button gets samename
value
value
(i.e., default entry)checked
- For
radio
&checkbox
elements - Set value = "checked" to pre-select/set a default
- For
- Values for
label
Element- Is Linked to
input
Element - Note: Value of
for
attribute is set equal to value ofinput
element'sid
attribute
- Is Linked to
select
Element (i.e., drop-down boxes)textarea
Element
- Presentation (Briefly...)
- Styling option:
- Inline declarations - i.e.,
style
attribute - Embedded style sheets
- External Style Sheets (i.e., CSS files)
- Inline declarations - i.e.,
- Linking to Style Sheets
class
attributeid
attribute- Custom attributes
- Use of
div
tags
- Styling option:
- Bibliography
- XHTML Basics
- CSS 2.1 (incomplete)
- CSS Basics
- Definitions & Syntax
- Style sheet - a collection of CSS rules (a.k.a. styles)
- Rule syntax
- Selector, followed by semi-colon-delimited declarations within curly brackets
- Declaration block syntax:
property: value
(though standard practice, space after colon is optional)- Values with embedded spaces are enclosed in quotation marks
- Style sheets
- Embedded
- Inside
<style>
element w/inhead
element type
attribute required withinstyle
tag, with value set to"text/css"
- Typically the last element inside the
head
declarations
- Inside
- External
- "Attach" via
link
element with web page'shead
element - Alternately, use CSS's
@import
directive withinstyle
tags - If using
@import
directive the option exists to then include embedded styles
- "Attach" via
- Best Practice: Rely almost exclusively on external style sheets
- Embedded
- Inline styles
- Declared using the
style
attribute within an HTML element - Syntax:
style="property: value;"
- Multiple style declarations can be included - semi-colon-delimited
- Inline styling is not a style sheet
- Declared using the
- Selector Basics
- Rule precedence
- A later rule supercedes a prior rule
- Specificity supercedes generality
- Page-wide styling
- Tag selectors or Type selectors
- Syntax:
element {declaration block;}
- Universal, class & ID selectors
- Universal selector:
*
(i.e., asterisk) - Class selectors
- "dot" notation
- Class names are case senstive & must start with a letter
- Can select by class within given elements - syntax:
element.class
- Id selectors (# sign)
- Pound sign notation (i.e.,
#idvalue
) - When in conflict, id selectors supercede class selectors
- Pound sign notation (i.e.,
- Universal selector:
- Styling tags within tags (i.e., descendent selectors)
- In CSS1 these were referred to as "Contextual Selectors"
- Current terminology is "Descendent Selectors"
- Syntax:
ancestor-element
descendent-element
, etc. - Can include multiple "generations" in the selector
- Do not need to list every ancestor generation (i.e., can skip generations)
- Can include & mix in class selectors & universal selector
- Grouping & combining selectors
- Group tags by delimiting with commas
- Combine tags by omitting white space between them
- Example 1:
p .intro
- the style defined here will apply to any element withclass="intro"
that is a descendent of a paragraph (p
) element - Example 2:
p.intro
- the style defined here will apply to the following element(s):<p class="intro">
- Example 1:
- Pseudo-classes & pseudo-elements
- For Links
a:link
(unvisited links) &a:visited
a:hover
(:hover
can alse be used with other elements)a:active
(a relic from the days of slow connection speeds)
- For paragraphs (primarily):
:first-letter
&:first-line
- Other (not supported by IE versions 6 & earlier)
:before
&:after
- These 2 selectors produce generated content
- Example:
p.tip:before {content: "INSIDE INFO!!"}
:first-child
- This is applied to the child element itself, not to the parent (e.g., to
li
instead of toul
) - This can be a tricky pseudo-element because an element may not be the first child of a parent element
- Behavior can change whenever the HTML/XHTML code itself is edited
- This is applied to the child element itself, not to the parent (e.g., to
:focus
- Analagous to
:hover
pseudo class fora
elements - Primarily used for form elements
- Analagous to
- For Links
- Advanced selectors (not supported by IE versions 6 & earlier)
- Child selector: greater-than bracket - e.g.,
ul > li
- Adjacent (following) sibling: plus sign - e.g.,
h1 + p
- Attribute selector
- Syntax: square brackets around attribute name - e.g.,
p[lang]
- Can also select based on an attribute having a specific value - e.g.,
p[lang="fr"]
- Attribute selectors can be applied to class selectors as well - e.g.,
.foreign[lang="fr"]
- Syntax: square brackets around attribute name - e.g.,
- Child selector: greater-than bracket - e.g.,
- Rule precedence
- Inheritance
- As a rule, elements inherit the styles of their ancestors (whether elements or styles)
- Where inheritance does not apply (generally):
- Properties that affect:
- Page placement (including margins)
- Background colors
- Borders
- When browsers have their own, inherent styles (e.g., the color for links, font size for headings)
- Properties that affect:
- A more specific style (i.e., selector) always supercedes any conflicting styles from a more general selector
- The Cascade - i.e., Multiple Styles
- Hybrid styles
- Inheritenced styles accumulate
- Styles can also accumulate from tag, class & id selectors
- Accumulated styles all apply so long as they do not conflict
- How conflicting styles are resolved
- When conflict arised from multiple inheritances:
- Nearest ancestor prevails
- A directly applied style prevails
- When conflict arises from multiple sources:
- Specificity prevails
- Point values for specificity
- Tag selector (& pseudo-elements): 1 point
- Class selector (& pseudo-classes): 10 points
- ID selector: 100 points
- Inline style: 1,000 points
- Points combine just as selectors do. Examples:
element.class
selector = 11 pointsclass element element
selector = 12 pointsid element
selector = 101 pointselement:pseudo-class
selector = 11 pointselement:pseudo-element
selector = 2 points
- In the case of an equal number of points between conflicting styles:
- The last/most recent rule prevails
- This rule even extends to whether an external style sheet is listed before or after embedded styles within a document's
head
element
- When conflict arised from multiple inheritances:
- Controlling the cascade
- Overriding specificity rules
- Use the
!important
flag - Note:
!Important
is applied to a property (i.e., inside the curly brackets)
- Use the
- Other options
- Rephrase rules to include more specific selectors
- For specific pages:
- Add an internal style sheet (after the
link
statement for the external style sheet) - Create an external style sheet just for a page
- In either case, list the "global" styles first within the
head
element
- Add an internal style sheet (after the
- For any & all pages, add an
id
to each page'sbody
tag
- Overriding specificity rules
- Hybrid styles
- Definitions & Syntax
- Commonly-Used Properties
- Color
- Notation
- Hexadecimal (# notation)
- RGB:
- Percentages - e.g.,
color: rgb(60%,70%,80%);
- 0-225 base - e.g.,
color: rgb(0, 100, 255);
- Percentages - e.g.,
- Color keywords
background-color
styles can be set for any (logical) element
- Notation
- Size Units
- Units most commonly used with text:
- Pixels -
px
- Pixels are especially common because sizing is not browser-dependent
- Downside: cannot be resized on older browsers, which presents an accessibility issue with text
- Ems -
em
- Originally (in typography) one
em
was the size of the capital letter M. - In CSS an
em
is essentially a percentage, where1em
means 100% of regular font size
- Originally (in typography) one
- Exs -
ex
- Pixels -
- Units most commonly used with borders, magins, etc.:
- Picas -
pc
(= 12pts) - Points -
pts
(=1/72 in) - Inches -
in
- Centimeters -
cm
- Millimeters -
mm
- Note: above units sometimes used with text on
printer
style sheets
- Picas -
- Units most commonly used with text:
- Color
- Block-level vs. Inline Elements
- Inline elements
- Can never contain block-level elements
- Can contain other inline elements
- Examples:
span
em
strong
cite
a
img
(even though it is an empty tag)
- Styling options:
- Text and background colors
- Any and all font properties
display: block;
allows block-level formatting to be applied to inline elevements
- Block-level Elements
- Create breaks before and after
- Can contain both other block-level elements and/or inline elements
- Examples:
h1, h2, h3
, etc.p
div
blockquote
ul
andol
form
- Styling options:
- Setting a fixed width or height for a block of text
- Setting a padding effect
- Moving to any position on a web page, regardless of the position in which the markup appears
- Inline elements
- Styling Text (Primarily...)
- Font Properties
font-style
italic
oblique
(same asitalic
)normal
(to reset an inherited font-style)
font-variant
small-caps
normal
(to reset an inherited font-variant)
font-weight
- Keyword properties
bold
bolder
lighter
normal
(to reset an inherited font-weight)
- Can also use numeric values
- Range: 100 to 900, in increments of 100
normal
= 400, andbold
= 700
- Keyword properties
font-size
- Available units:
- All measurement units, as listed in the Size section
- Note 1: Standard font size for non-headings text is generally 16 pixels
- Note 2: Unless the user has tweaked his browser's default font settings, 2.4em should work out to be 24 pixels tall
- Absolute size: 7 values, from
xx-small
throughxx-large
- Realitive size:
larger
orsmaller
(2 values only) - Percentages
- Note 1 (on using percentages and ems):
- Because of inheritence, setting
font-size
to, say, 90% or .9em when an ancestor already has the same style will result in a net font-size of 81% or .81em. - This especially arises when using percentages and ems with nested lists. Without taking care with selectors, texts in the nexted lists will automatically change exponentially.
- Because of inheritence, setting
- Note 2 (a trick with percentages):
- Since base font is 16 pixels, setting
body {font-size: 62.5%;}
changes the base font to 10 pixels - It then becomes much easier to work with ems or multiples to adjust font sizes
- Since base font is 16 pixels, setting
- Available units:
font-family
- Can declare multiple, comma-separated values
- Common combinations:
Arial, Helvitica, sans-serif
"Times New Roman", Times, serif
"Courier New", Courier, monospace
Georgia, "Times New Roman", Times, serif
Verdana, Arial, Helvitica, sans-serif
Geneva, Arial, Helvitica, sans-serif
Tahoma, "Lucinda Grande", Arial, sans-serif
"Lucida Console", Monaco, monospace
"Marker Felt", "Comic Sans MS", fantasy
"Century Gothic", "Gill Sans", Arial, sans-serif
- Shorthand notation - i.e.,
font
:- Order of values:
font-style
font-variant
font-weight
font-size
line-height
(preceeded by a "/")font-family
- Except for multiple
font-family
values, other values not comma-separated
- Order of values:
- Text Properties
word-spacing
&letter-spacing
(can use negative values to tighten spacing)text-decoration
(this property is not inherited)underline
overline
line-through
blink
normal
(to turn off a defaulttext-decoration
- most notably for links)
vertical-align
- Most commonly applied to text within table cells
- Not inherited
- Values:
- Keywords for use in tables:
top
baseline
- Very similar to
top
- Aligns the baseline of the first line of text of each cell in a row
- Effect generally indistinguishable from using
top
- Very similar to
middle
bottom
- Keywords for use with "normal" (i.e., inline) text
sub
super
text-top
text-bottom
- Units
- Can take positive and negative values
pixels
orems
- A percentage (calculated against the element's
line-height
value)
- Keywords for use in tables:
text-transform
uppercase
lowercase
capitalize
normal
(to reset an inherited text-transform property)
text-align
- Inherited property
- Values:
left
right
justify
center
text-indent
- Sets first-line indents for paragraphs
- Typically use
pixel
andem
values as the unit - Using negative units creates a "hanging" first line
- Percentages can be used as the value
- Percentage is not a percentage of the standard font
- Percentage relates to the overall width of the containing element
line-height
(for multi-line chunks of text)- Can use pixels, ems, percentages or a multiple (of the font size)
- Note: a drawback of using pixels is that the spacing will not adjust if the font size changes
- Multiples do not take units - e.g.,
line-height: 1.5;
sets the line height at 150% the height of the font
- Default line-height setting for most browsers is 120%
- Difference between percentages and multiples
- With percentages descendent elements inherit a calculated line height, not the percentage
- Example:
- With a normal font height of 12 pixels and a line height of 150%, descendent tags inherit a line height of 18 pixels
- If you then set font size on a descendent element to 36 pixels, lines will actually overlap
- With multiples, however, it is the multiple, not the calculated line height, which is passed to descendent elements
- Can use pixels, ems, percentages or a multiple (of the font size)
- Styling Lists
- Change marker with
list-style-type
attribute- Values for unordered lists:
circle
disc
square
none
- Values for ordered lists:
decimal
decimal-leading-zero
(not all browsers display this correctly)lower-roman
upper-roman
lower-alpha
upper-alpha
lower-greek
- Can apply properties either to
ul/ol
elements, or toli
elements - Can mix & match styles within a single list
- Values for unordered lists:
- For custom markers use
list-style-image
property- Value takes the form:
url(http://xyz.com/some_icon.png)
- To style for possibility of an image being unavailable:
- Use
list-style
attribute. - Provide property value in the form:
url(http://xyz.com/some_icon.png) disc
- Use
- Value takes the form:
- To control marker position relative to text
- Use
list-style-position
property - Possible values are
inside
oroutside
(default). - When using
outside
can addpadding-left
attribute to control space between bullet and text - Note: can also work with
margin-left
property to adjust positioning of total list
- Use
- Alternate method of creating custom markers
- Use
background-image
property - Note: this approach allows for greater control of marker positioning relative to text
- Use
- To format marker differently from text
- Format list or lines with the style that you want to apply to the markers
- Enclose text for each
li
withinspan
elements, and apply a style to thosespan
elements viaclass
selector)
- Shorthand notation (e.g.,
list-style: upper-roman inside;
)
- Change marker with
- Font Properties
- Styling Block-level Elements (Primarily...)
- Block-level Box Model
- Outside-in areas: margin, border, padding, & element
- Block width =
margin
(l&r) +border-width
(l&r) +padding
(l&r) + width of element (i.e.,width
) - Block height = similar to block width
- Block-level Properties: Margins, Padding & Borders
- Size/width properties (all three elements)
- Sizes/widths can be set individually - e.g.,
border-top, margin-right
, etc. - Shortcut notations
- Example 1:
padding: 5px 10px 0 10px;
- Order of sides for short-cut notation is top, right, bottom, left (mneumonic: clockwise starting at 12:00)
- Example 2:
margin: 5px;
- this means a 5 pixel margin on all 4 sides
- Example 1:
- Sizes/widths can be set individually - e.g.,
- Size/width values (all three elements)
- Any available unit can be used, though pixels and ems are most common.
- Using percentages
- Percentages are those of the width of the containing element (e.g.,
body
ordiv
) - This applies even with top and bottom borders (i.e., the percentage is still of the containing element's width)
- Percentages are those of the width of the containing element (e.g.,
- Borders
- Additional keyword values for width:
thin, medium,
orthick
border-style
(these can be set for each side - e.g.,border-top
)solid
(default value)groove
ridge
dotted
dashed
double
inset
outset
none
&hidden
- Effect is the same
none
tends to be useful for turning off a single border
- Can also set color with
border-color
- Note: Background colors & images go behind borders and therefore show through non-solid borders.
- Shorthand notation (e.g.,
border: 5px solid black;
, though order does not matter)
- Additional keyword values for width:
- Margins - special concerns
- Colliding margins (1)
- Top & bottom margins between vertically aligned elements are not additive
- Vertical space between two elements is set by the element with the larger applicable top or bottom margin
- To create "additive margins" use padding properties instead
- Colliding margins (2)
- Can run into problems when you have one element inside another borderless element
- Top & bottom margins of the inner element can often extend outside the container element
- Solution: use top and bottom padding of the container element
- Unlike borders & padding, margins accept negative values
- This will cause an element to "poke& beyond its otherwise available constraints
- Useful for some creative effects
auto
value- Can be used on any and all sides
- Setting left & right margins to
auto
effectively centers an element
- Colliding margins (1)
- Size/width properties (all three elements)
- Inline vs. Block-Level Boxes
- Top & bottom margin & padding settings
- These are ignored for inline elements (left & right settings do apply)
- One exception to this: images
- To adjust verticle spacing for inline elements must use
line-height
settings
display
property- To have an inline element behave like a block-level element set
display: block;
- Conversely, can have a block-level element behave like an inline element via
display: inline;
- Note: Setting diplay to
none
hides an element (and closes up the otherwise-resulting empty space)
- To have an inline element behave like a block-level element set
width
&height
- These properties apply to the contained element within a block
- Width default = 100% of parent container's width
- Height default = whatever is required for contained element
- Allowable values:
- Any available unit can be used
- Note: Percentage are of containing elements width & height
- Auto
- Max/min properties:
min-height, mid-width, max-height
&max-width
- These values are useful in flexible layouts
- Also useful regarding very small or very large monitors
overflow
property- Governs how a browser handles situations where content (usually text) does not fit into it's container element's specified dimensions
- Keyword values
visible
- allows the contents to spill out of the container element in order to be displayedscroll
- Adds horizontal and vertical scroll bars for viewing content
- Drawback: scrollbars are added even when the content does fit inside the containing element
auto
- same asscroll
but only adds scollbars if/when neededhidden
- hides any content which does not fit (this has obvious risks)
- Top & bottom margin & padding settings
- Graphics
- Inline images
- Can set
height
andwidth
properties
- Can set
- Background images
- Need to specify
background-color
even when including abackground-image
, in the event the image does not load. background-image
property- Syntax (absolute path):
background-image: url(http://williamhwhite.biz/images/somepattern.gif)
- When using relative paths in url, browsers search in relation to location of CSS (not HTML) file
- Image file locations can be enclosed in single or double quotes (or not at all) within the parentheses of the url value
- Syntax (absolute path):
- URL path types (see discussion of URLs in my HTML Knowledge Base outline)
- Values for
background-repeat
:repeat
(default value)repeat-x
(i.e., horizontal repeats)repeat-y
(i.e., vertical repeats)no-repeat
- Values for
background-position
property:- Horizontal:
left, enter,
orright
- Vertical:
top, center,
orbottom
- Can also use percentages and lengths (i.e., ems and pixels). Horizontal is specified first, vertical second.
- Horizontal value: distance between left edge of image & left side of container element
- Vertical value: distance between top edge of image & top of container element
- Can use negative values
- Percentages
- Aligns the percentage width & height of the image with that same percentage width & height of the containing element
- Still measured from left & top edges
- Note: most browsers have trouble handling styles with mix keywords (e.g.,
top, right
) with percentages, ems or pixels - When used in combination with
background-repeat
thebackground-position
property controls the placement of the first tile to be repeated - Firefox bug (& fix) when page elements don't fill the entire height of a browser screen
- Firefox calculates the bottom of the page as the bottom of the lowest element
- Background images styled with
background-position: bottom;
are therefore not placed at the bottom of the browser window - Fix: add
html {height: 100%;}
as a style
- Horizontal:
- Locking an image in place
background-attachment
property- Values:
scroll
(default) &fixed
fixed
tends to work especially well with:- Logos
- Tiles backgound images
- Shorthand notation
background
property (values are listed without commas)- Order of values
backgound-color
backgound-image
backgound-attachment
backgound-position
- Values can be omitted
- Need to specify
- Inline images
- Block-level Box Model
- Formatting Tables & Forms (& Navigation Bars)
- Tables
- Application of block-level properties, such as text, borders, margins, etc.
- Padding properties only exist for
th
&tr
elements - not for thetable
element itself - Border properties of
table
element does not affect intra-cell borders - General approach
- X/HTML provides styling attrubutes, (e.g.,
border
for most table-related elements - Typically preferable to use CSS styling
- X/HTML provides styling attrubutes, (e.g.,
- Padding properties only exist for
- To control spacing between cells
- CSS 2.1 provides
border-spacing
property, though no version of IE supports it - Better approach:
- Use
cell-spacing
attribute oftable
element - If
cell-spacing
is set to 0, typically want to setborder-collapse
property as well:- Setting value to
collapse
eliminates double-thickness borders - Other value for
border-collapse
isseparate
- Setting value to
- Use
- CSS 2.1 provides
- Text alignment within table: see text properties section of this (CSS 2.1) outline
- Accessibility techniques
- Use of
summary
attribute withintable
element scope
attribute- Values:
row
&col
- Controls linearization - i.e., how a reader processes table data
- Especially useful for tables with header cells along both top & side
- Values:
- Use of
- Application of block-level properties, such as text, borders, margins, etc.
- Forms
- Styling Links & Building Navigation Bars
- "standard" alternate link-styling options
- Remove default underline (
a:link {text-decoration: none;}
) - Use
a:hover
property to re-introduce an underline - Use a bottom border for to expand underlining options
- Use a background image
- Note: browser default is to add a 1px border around an image-as-link
- Override with
a img {border: none;}
- Note: for links > 1 line background images will only appear behind last line
- Create a 'button'
- Employ
border, background-color
&padding
properties - Typically want to set the
a:hover
pseudo-style - For 3-D effect:
- Set border on one side with lightest shade (the 'lit' side)
- Set opposite border with darkest shade (the side 'in the shadow')
- Set shade for remaining 2 borders in-between
- For
a:hover
pseudo-style generally want to flip/reverse border shadings
- Employ
- Large clickable 'buttons'
- The
:hover
pseudo-class is applicable to numerous elements (e.g.,p, div
, etc.) - IE Note: versions 6 and earlier, however, do not recognize this
- Applying block styling to links
a
(anchor) is an inline element so it cannot contain block-level elements- Workaround:
- Use
span
tags within anchor text - You can set the display property of a span to
block
, however - This then allows more flexible formatting
- Use
- The
- Remove default underline (
- Cascade concerns when using
a
(i.e., anchor) pseudo-classes- Must use the following order:
a:link
a:visited
a:hover
a:active
(i.e., if used)
- If the order is not followed
a:hover
(anda:visited
, if used) states will not work
- Must use the following order:
- Navigation bars
- Typcially built using
ul
tag(s) - For stylized navigation bars typically:
- Remove bullet icons (
list-style-type: none;
) - Eliminate padding and margins (browsers vary on using padding vs. margins to indent list items)
- Remove bullet icons (
- Building vertical navigation bars
- Display links as blocks (
display: block;
)- Only way to set common widths
- Also, inline elements have no top & bottom padding & margin properties
- Makes entire box/block area clickable
- To center one-line link text vertically set
height
andline-height
properties ofa
tags to same value - Note: IE, including ver. 7, inserts gaps between links unless
width
property ofa
tags has been set
- Display links as blocks (
- Building horizontal navigation bars
- Approach 1: set
display
property ofli
elements in list toinline
- Note: links will vary in width
- Cannot add padding or borders to
li
elements. Can, however, do so forul
container
- Approach 2: floats
- Float
li
elements left or right - Set
display
property ofa
elements toblock
- This provides control of width of each link (ems are the preferrable unit to use)
- Also provides for vertical white spacing
- Float the containing
ul
element - Can set
clear
property forul
container and/or for next element following
- Float
- Approach 1: set
- Typcially built using
- "standard" alternate link-styling options
- Tables
- Page Layout
- Float-Based Page Layouts
float
controls where to position an element relative to adjacent text.- Values:
left, right,
ornone.
- Note: a floated item is moved to the edge of its container element, which may well not be the browser screen itself
- Floated elements (if inline) are treated as block-level elements.
- Use of floated element's
margins
to control space between item and surrounding text. - Use container text's
pad
values to prevent L-shaped wrap-around. - With floated block-level elements, typically want to specify
width
- Note: within the HTML mark-up, floated element must appear before any content that will wrap around
- Values:
- Backgrounds, borders & floats
- While text flows around a floated element, backgrounds and borders do no
- Result: Backgrounds and borders of "wrapping" elementsappear underneath floated elements
- Solution: use
overflow: hidden;
style for the "wrapping" element
clear
property- Controls the side(s) on which another element is not allowed to float.
- Values:
left, right, both,
ornone
. - Useful when you have an element that you do not want to wrap around, or appear next to, a floated element (e.g., a page footer)
- Also useful when you have several narrow, floated elements and you do not want them to stack vertically, not horizontally
- Positioning Elements
- Postioning Options
position: absolute;
top:
,left:
, etc.- Absolute positioning positions elements in relation to their containing elements.
position: relative;
top:
,left:
, etc.- Absolute positioning positions elements relative to where they would otherwise appear.
- Float-Based Page Layouts
- IE Quirks & Hacks
- General Syntax
- IE 6 & earlier
* html
- This is non-sensical to non-IE browsers & is ignored
- This must follow other, "normal" style declarations
- This hack is not understood by IE 7 (though IE 7 fixed many IE 6 quirks & bugs)
- IE 6 & earlier
- General Syntax
- Bibliography
- Lloyd, Ian. Build Your Own Web Site The Right Way Using HTML & CSS. United States of America: SitePoint, 2006. Print.
- McFarland, David Sawyer. CSS: The Missing Manual. United States of America: O'Reilly Media, Inc., 2006. Print.
- CSS Basics
- DOM Scripting - i.e., JavaScript 1.5 (draft; incomplete)
- JavaScript (JS) Syntax
- HTML Declarations
- JS placed within
head
section execute when called - JS placed within
body
section execute while document loads - For external JS set
type
andsrc
attributes ofscript
- JS placed within
- Statements
- Curly brackets
- Semi-colons
- Comments
- Single line: //
- Multi-line: /* and */
- HTML-style comments also allowed
- Variables
- Naming restrictions
- No spaces
- No punctuation except for the dollar symbol, $
- Numbers and underscores are allowed
- Variable declaration via
var
keyword - JavaScript does not allow/provide strongly-typed variable declarations
- Variable asignment with "=" operator
- JavaScript allows simultaneous declaration and assignment
- JavaScript allows assignment without prior declaration
- Best Practice: always declare variables prior to, or in conjunction with, assignment
- Naming restrictions
- HTML Declarations
- JavaScript Basics
- Data Types
- Scalars (i.e., variable holds only one value at a time)
- Strings
- Declared via use of matching single or double quotes
- Character escaping for single or double quotes within a string via slash ("\") character
- Numbers (all are floating-point)
- Booleans
- Strings
- Multi-value variables
- Arrays
- Defined via
Array
keyword - Zero-based indices
- Syntax options
- Declaration with length: e.g.,
var beatles = Array(4);
- Declaration without length: e.g.,
var beatles = Array();
- Assignment: e.g.,
beatles(0) = "John"
- Declaration & assignment: e.g.,
var beatles = Array("John", "Paul", "George", "Ringo");
- Declaration & assignment: e.g.,
var beatles = ["John", "Paul", "George", "Ringo"];
- Declaration with length: e.g.,
- Arrays of arrays
length
property used to return or set array size
- Defined via
- Associative Arrays
- Override default assignment of index numbers to array values with keywords
- Comparable to dictionary declarations
- Syntax:
var beatles = Array(); beatles("drummer") = "Ringo"
- Arrays
- Scalars (i.e., variable holds only one value at a time)
- Operations
- Arithmetic operators
- Addition, subtraction, multiplication, & division
- Modulus (i.e., remainder after division) via
%
operand - Increment & decrement shortcuts:
++
and--
- Concatenation also uses "
+
" operator - Simultaneous additon-and-assignment or concatenation-and-assignment:
+=
- Conditional statements
if
statementsif…else
statementsif…else if…else
statementsswitch
statementscase:
statements for conditionsbreak;
statement used at end of each set ofcase:
conditionsdefault:
statement for "else, otherwise" conditions
- Syntax
if (condtion) {statements;}
if (condtion) {statements;} else {statements;}
- Comparison operators
- 4 basic:
<
,>
,<=
, and>=
- Double equals for equality - i.e.,
==
- Triple equals for exact equality - i.e., both value and type
- Inequality:
!=
- 4 basic:
- Logical operators
- And:
&&
- Or:
||
- Not:
!
- And:
- Arithmetic operators
- Looping Statements
while
do…while
for
for
. Syntax:for (initial condition; test condition; alter condtion) { statements;}
for…in
. Loops through elements of anarray
or properties of an object
- Functions (& Variable Scope)
return
keyword within function statements to return a value- Functions do not have to return values (i.e., can be procedure)
- Functions can accept arguments
- Scope
- Variables can have either local or global scope
- To declare a variable with local scope (i.e., within a function) must declare using
var
keyword. - Even when occuring within a function, variable declarations sans
var
are globally scoped.
- Objects
- Objects and "methods" and "properties"
- Dot syntax - i.e.,
object.property
andobject.method()
- Object instantiation
- Native objects
- Objects embedded in JavaScript itself
- Examples:
Array, Math
&Date
- Host objects
- Objects provided by browser
- Examples:
Window, Document, Form, Image
, &Element
- User-defined objects
- Data Types
- Javascript Objects
Date
- 18
"get"
methods (e.g.,getDay(), getDate(), getFullYear(), getMonth()
) - 15
"set"
methods (e.g.,setDate(), setHours(), setMinutes(), setTime()
) - 7 versions of
toString()
method get, set
&toString
methods all available in local-time and UTC versions- Instantiate with
new
keyword
- 18
- Pop-up Boxes
alert
box (has an "OK" button)confirm
box- Has "OK" and "Cancel" buttons
- "OK" button returns
true
, "Cancel"false
prompt
box - for obtaining user input- Two arguments: "Prompt" text and "default" input
- "OK" button returns user input
- "Cancel" button returns
null
- The Document Object Moded (i.e., DOM)
- Nodes
- Nodes follow XML specification (see my XML outline for a more extensive dilineation)
html
element is the "root" node of the DOM- Node types (i.e., element, text, attribute, & comment)
- Use of "dot" notation to "drill down" into document tree - e.g.,
document.body.h1
- DOM Methods
getElementById
(parameter ="id"
, in quotation marks)getElementsByTagName
- Returns an "array" of "objects"
- Can use wildcard charater as a parameter - e.g.,
document.getElementsByTagName("*")
getAttribute
setAttribute
- Nodes
- Bibliography
- Flannagan, David. JavaScript: The Definitive Guide. 5th Ed. United States of America: O'Reilly, 2006. Print.
- Powers, Shelley. Learning Javascript. United States of America: O'Reilly Media, Inc., 2007. Print. This is a perfectly dreadful book. See my review of this montstrosity on amazon.com.
- JavaScript (JS) Syntax
- HTML 4.01
- XML & Related
- XML 1.0 (incomplete)
- XML Names
- XML is case-sensitive
- Allowable characters
- All Latin upper- and lower-case letters
- Accented letters - e.g., Ç, é
- Characters from non-Latin scripts, such as Greek or Arabic
- Digits (i.e., 0-9)
- Only 3 punctuation marks:
- Hyphen "-"
- Underscore "_"
- Period "."
- Allowable first characters
- Letter, whether Latin or non-Latin
- Underscore
- Whitespace (i.e., tab, newline or space)
- Must not appear between opening bracket and first character of element name
- Otherwise perfectly allowable - & sometimes even required (e.g., to separate attribute/value pairs)
- Standard Practice: markup written in lower case (camel case for long names?)
- XML Document Structure & Syntax
- Prolog
- A prolog is optional
- If used a prolog must precede the document element
- Prolog elements (all optional)
- XML Declaration
- If used it must be the first line in a document
- Parameters
version
- must always be included in the XML declarationencoding
(default = UTF-8)standalone
- allowable values are"yes"
or"no"
(default)- When used, these parameters must appear in order
- Example:
<?xml version="1.0" encoding='UTF-16' standalone = "no"?>
- Document Type Declaration
- Rationales for including (two):
- Define entities or default attribute values
- To support validation
- Syntax
- Delimiters
- Opening:
<!DOCTYPE
- Closing:
>
- Opening:
- Components
- Element name
- DTD Identifier (optional; see below)
- Declarations (optional)
- List of such declarations enclosed within square brackets
- A.k.a. the internal subset
- Example:
<!DOCTYPE someElementName someDTDIdentifier [
someInternalDeclaration_1st
someInternalDeclaration_2nd
…
]>
- Delimiters
- DTD Identifier - methods of identification:
- System-specific
- Syntax:
SYSTEM "someSystemIdentifier"
- System identifier = path or URI
- Example:
SYSTEM "myWebSite/myDtds/someFile.dtd"
- Syntax:
- Public
- Theory: public identifier should never change
- Problem: there is no "public registry" to make them unique
- Practice: include a URI as a "backup" public system identifier
- Syntax:
PUBLIC "unique public identifier"
"back-up public identifier" - Example: (inside a full document type declaration)
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">
- System-specific
- Declarations (general)
- Pieces of information needed to assemble & validate XML document
- External declarations: provided by DTD Identifier
- Internal declarations: those contained within the square brackets of a document type declaration
- External declarations are read before internal ones
- Various kinds - for validation, defining entities, style sheets, etc.
- Rationales for including (two):
- XML Declaration
- Entity declarations
- General concepts on entities
- Entity = a named value
- Entity names can be thought of as either shorthand notation or as placeholders
- Named value
- Can be anything from a single character to an entire file
- A piece of XML that can be inserted anywhere inside the document
- Note – syntax for using an entity reference:
&EntityName;
- Entities can be nested - i.e., a referenced entity can itself contain references to other entities
- Unless predefined all entities must be declared before being referenced
- Note: with nested entity declarations one can put the parser in an infinite loop if such declarations do not appear in the proper order (i.e., before the relevant entity reference)
- Placement (i.e., where entities can be declared)
- Within document prolog
- Within a DTD
- Entity types
- Parameter
- Used only in DTDs
- 2 types
- Internal
- External
- See Document/Data Integrity outline below
- General
- Character
- Predefined (5)
- amp (i.e.,
&
) - & - apos (i.e.,
'
) - ' - gt (i.e.,
>
) - > - lt (i.e.,
<
) - < - quot (i.e.,
"
) - "
- amp (i.e.,
- Numbered (for Unicode characters)
- Decimal syntax:
&#decimalNumber;
- Hexadecimal syntax:
෬imalNumber;
(note the "x")
- Decimal syntax:
- Named
- Beyond the 5 predefined character references named character references must be declared
- O'Reilly's www.xml.com has the best discussion I have seen on subject
- There are also a number of character entity sets provided by OASIS, though I can vouch for none of them
- Note: In XHTML one automatically inherits the 100+ named character references declared in HTML. (W3C also, of course, provides a list of these character references).
- Predefined (5)
- Mixed-content (i.e., anything beyond a single-character entity)
- General
- No limit on length
- Can include both markup and text/content
- Internal
- Replacement text defined in the entity declaration
- Generally used for oft-repeated words & phrases
- Can improve 'quality control'
- As a rule do not use any of the 5 predefined entity references inside the entity value - i.e., simply include, say, the
<
or>
characters themselves - Exception: If entity value is delimited with single quotation marks then must use
<
to escape any single quotation marks within the entity value itself. (Same logic & approach for double quotation marks.)
- External
- Replacement text in an external file
- Useful for large amounts of text
- Useful where entity references may appear in multiple documents
- Useful for 'breaking down' large documents into component parts
- Best Practice: if using to create a master document/sub-documents arrangement have sub-documents contain only 1 document element of its own
- External text is inserted at the time of parsing
- General
- Unparsed
- An entity that is neither text nor XML - e.g., an image
- Can only be used from within an attribute value
- Syntax
- Example:
<!ENTITY mypic SYSTEM "images/somepic.gif" NDATA GIF>
NDATA
keyword indicates that entity is not to be parsed- Notation identifier specifies data format
- Example:
- Character
- Parameter
- Syntax:
<!ENTITY someEntityName someIdentifierOrValue>
- Identifier can be either a system or a public one
- Syntax for the identifier portion of an entity declaration: same as that used for DTD Identifiers
- 3 types of identifiers
- System
- Public
- Explicit
- Examples:
- With
SYSTEM
identifier:
<!ENTITY xmlKb SYSTEM "myXmlKnowledgeBase.xml">
- With explicit identifier:
<!ENTITY crSpan "<span property="dcterms:creator">">
- With
- General concepts on entities
- Document element (aka, the "root element")
- XML document = a collection of nested elements
- XML document must have one - but only one - document/root element
- Multiple files can comprise an XML document
- Implication: an XML document is a logical, not a physical, entity
- Allowable End-of-Document Text
- Comments
- Processing instructions
- Prolog
- XML Object Types
- Element (2 types) - incl. Attributes & Namespaces
- Attributes
- Attributes are name/value pairs
- Syntax
- Attribute values must be enclosed in matching single or double quotation marks
- Attribute name & its value(s) paired via an equals(=) sign
- Example (single attribute value):
attribName="value"
- Example (multiple attribute values):
attribName="value1 value2 value3"
- Within element markup multiple attributes separated by white space
- Note:
id
attribute- Value of the
id
attribute must be unique within the document - Value of an
id
attribute is considered anIDTOKEN
- Value of the
- Element types (2)
- Container elements
- Empty elements
- Syntax
- Syntax ("container element"):
<elemName attrib1="value1" attrib2="value2" … >someContent</elemName>
- Syntax ("empty element"):
<elemName attrib1="value1" attrib2="value2" … />
- Syntax notes
- With container elements, element name of closing tag must exactly match (i.e., case-sensitive) element name of opening tag
- Best Practice with empty tags:
- Leave a blank space before the closing slash
- Example:
<developer name="Wm White" />
, not<developer name="Wm White"/>
- Syntax ("container element"):
- Namespaces
- Mechanism for grouping element & attribute names
- Namespaces define/refer to a vocabulary
- Namespace declarations
- General
- Placed inside XML elements
- Apply to element & all its descendants
- Any one element can contain multiple, whitespace-delimited namespace declarations
- Syntax
- When including a namespace prefix (usually a short abbreviation of some sort):
xmlns:someNsPrefix="someURI"
- When omitting a namespace prefix (thus making the declared namespace the default namespace):
xmlns="someURI"
- Note: Using
xml
as a namespace prefix will lead to tears
- When including a namespace prefix (usually a short abbreviation of some sort):
- Prefixes
- Advantages
- As tokens or replacements for the full namespace prefixes are much shorter and more readable
- Namespaces may well contain characters not permitted in XML names
- Names governed by rules for XML non-colonized names
- Common namespace prefixes
xsd
&xs
for XSDL, the XML Schema namespacexsl
for Extensible Stylesheet Languagexsi
for the XML Schema Instance namespacexlink
for XLink
- Advantages
- Default namespace
- You are allowed one
- Define by omitting a prefix
- Default namespace declarations do not apply to attributes
- Empty string namespaces
- You can map the default namespace to an empty string
- Rationale: you want to disable an inherited default namespace declaration
- You cannot, however, map prefixes to an empty string
- URIs in namespace declarations
- Common Practice: use URL subset of URIs
- Drawback (of using URLs): URLs can change
- In Practice: XML does not, in fact, actually look up the information at the URL
- purl.org
- purl - acronym for permanent uniform resource locator
- One may register a URL on purl.org and then have the purl.org URL 'point to' the site where a resource is actually located
- One may then move the resource, but so long as the pointer is updated within purl.org's account information the purl.org-URL will continue to function without any change noticed by end-users
- purl.org URLs used in a number of notable semantic web vocabularies
- General
- Including a namespace element or attribute
- When referring to the default namespace - essentially, nothing needs to be done
- A fully qualified name
- Must be used for namespaces other than the default namespace
- Syntax:
namespacePrefix:namespaceLocalName
- Prefix assigned to a namespace can be overridden with a new namespace declaration
- Examples
- Declaring & using multiple namespaces:
<bibliography xlmns:dcterms="http://purl.org/dc/terms/"
xlmns:bibo="http://purl.org/ontology/bibo/"
xmlns:addr="http://schemas.talis.com/2005/address/schema#">
<bibo:book bibo:isbn10="123-456-7890">
<dcterms:title>War and Peace</dcterms:title>
<dcterms:creator>Leo Tolstoy</dcterms:creator>
<publishingInfo>
<addr:countryName>Russia</addr:countryName>
</publishingInfo>
<bibo:book>
</bibliography> - Overriding a previously-declared namespace:
<desert:name
xlmns:desert="http://njdiners.org/deserts/">
apple pie
<desert:presentation
xlmns:desert="http://fauxfrenchterms.org/cooking/">
a la mode
</desert:presentation>
</desert:name>
- Declaring & using multiple namespaces:
- Namespaces & validating XML
- Generally XML with non-implicit namespaces will fail to validate
- Use/existence of multiple namespaces in a document violate the constraints of the document's DTD
- The XML namespace itself
- Whitespace
- Many parsers will normalize whitespace
- Strips out whitespace in element-only content
- Removes whitespace from the beginning & end of mixed content
- Collapses a sequence of whitespace characters to a single space
xml:space
attribute- setting value to
preserve
(i.e.,xml:space="preserve"
) is supposed to (obviously) preserve whitespace - Note: the
xml
prefix does not require an explicit namespace declaration
- setting value to
- Many parsers will normalize whitespace
- Conceptual organization of XML elements
- Trees
- XML documents have an upside-down tree structure
- Branches & leaves
- Genealogy
- Parent & child relationships
- Ancestor & descendants
- Siblings
- Trees
- Attributes
- Declaration
- Stylesheet declaration
- CSS
- Others (esp. XSLT)
- Stylesheet declaration
- Processing Instruction
- Best Practice: avoid processing instructions wherever possible
- 2 components
- Target keyword
- Data
- Syntax
<?someKeyword someData?>
- Neither target keyword nor data can contain the closing delimiter (i.e.,
?>
) - A data string is optional (data string typically omitted when inserting, say, line- or page-break instructions)
- Target = keyword designed to be recognized by a specific XML processor
- Comment
- Syntax
- Same as HTML - i.e.,
<!-- someComment -->
- Comment itself cannot contain two consecutive dashes (this implies that you cannot nest comments)
- Same as HTML - i.e.,
- Placement
- Cannot appear before XML declaration line
- Cannot appear inside elements
- Can appear inside element content
- Syntax
- CDATA section
- Essentially tells parser that there is no markup in the section
- Syntax
<![CDATA[ content you don't want parsed ]]>
- CDATA content cannot contain the CDATA ending delimiter - i.e.,
]]>
- In fact, cannot use the CDATA ending delimiter (
]]>
) anywhere in XML document text
- Useful for text that contain several instances of XML's 5 predefined entities
- Entity Reference
- Syntax:
&entityName;
- See section on entity declarations, above
- Syntax:
- Element (2 types) - incl. Attributes & Namespaces
- Data Modeling/Logical Structures
- Simple Data Storage
- Dictionaries
- (Strikes me that this syntax would require a special DTD in the DOCTYPE declaration)
- One-to-one mapping of properties to values
- A property is a key (value s/b unique)
- Data essentially stored in a two-column format
- Syntax (periods added for spacing):
<developer name="Bill White">
<dict>
<!-- KEY . . . . . . . . . . . . . . VALUE -->
<key>KnowsXml></key>. . . . . . . . <true />
<key>Our1to5StarRating</key>. . . . <integer>5</integer>
<key>GivenAge</key> . . . . . . . . <integer>37</integer>
<key>LiesShamelesslyReAge</key> . . <true />
</dict>
</developer> - Dictionaries can be nested - i.e., value for a given key can be another dictionary
- Can assign an array as a key's value
- Records
- Database records can be thought of as dictionaries-with-multiple-values (databases almost always have ID - i.e., key - fields)
- Database fields are the same from one record to next - though not all fields require values for each record
- In Ray's book…
- The key element contains sub-elements but no content of its own
- The key element does contain attributes and values, though
- XML & databases
- XML very good at modeling simple data structures
- XML easier to modify than a flat file
- XML is not good for data requiring rapid, repetitive access
- XML parsers read entire document to find even one data point
- XML and databases can be combined
- One can often store an XML document within a relational database data field
- Step 1: store the unique id/key in the appropriate relational database key field
- Step 2: store XML markup (i.e., the key's value) "defining" that key in a second database field
- XML elements from one record to next should be essentially identical
- Advantage: allows one to make use of relational database search capabilities for much faster searching
- Disadvantage: by storing data within XML elements relational database search capabilities generally cannot be utilized
- Alternative: use relational database as you would normally, but when retrieving data via a query, etc., format the data output as XML
- Dictionaries
- Narrative Documents
- Flows & sections
- Flow: a stream of text to be read continuously from start to finish
- Every narrative document has at least one flow
- When multiple flows exist one is dominant, the others tangential
- Ancillary flows generally placed in boxes, sidebars, or at the end of the document
- Marking up flows
- Some applications - e.g., XHTML - only support 1 flow
- DocBook has extensive support for flows
- Flows - esp. the main flow - often broken up into sections
- Sections usually exist in a hierarchy
- Sections usually encompass a single topic
- Sections usually have titles or heads
- Sections often exist in their own XML document
- A master document will then pull in the sections/sub-documents
- Section structure types (assume using element names
section, bighead, head
¶graph
)- Flat
- Elements occur in sequence
- Essentially relies sequence itself (i.e., presentation details) to discern structure
- XHTML is a flat structure
- Hierarchical
- Elements are nested
- Easier to discern structure
- DocBooks is a hierarchical structure
- Flat
- Flow: a stream of text to be read continuously from start to finish
- Blocks & inlines
- Block contains a segment of a flow
- Typically formatted in a rectangular region
- Holds both elements and character data
- Examples: paragraphs, section head, & list items
- Inline elements
- They only contain content
- Used for formatting
- Also used to mark content for software that, for example, will generate an index or table of contents
- Block contains a segment of a flow
- Complex structures
- Those structures which are neither blocks nor inlines
- Examples: tables and lists
- Complex structures usually remain inside one of the flows
- In terms of visual layout complex structures often float
- Metadata
- Information about the document itself
- Metadata is not part of the flow
- Often unformatted, though a Table of Contents, for example, is essentially metadata
- In XHTML the entire
head
element is, of course, metadata - In DocBooks individual sections can have their own metadata
- Linked objects
- These act as bookmarks in a document
- Types
- Cross-reference
- Can be a hyperlink
- Can also be essentially an entity reference
- Invisible marker (used, for example, to generate an index)
- Cross-reference
- XHTML
- DocBook
- Flows & sections
- Complex Data
- General
- XML handles complex data very well
- Multimedia examples: SVG & SMIL
- Conceptual examples:
- MathML (equations)
- Chemical Markup Language (chemical formulae)
- Molecular Dynamics Language (molecular interactions)
- Elements as objects
- XML components - elements & attributes - "map" nicely to objects & properties
- With DTDs and Schemas you can also check
- That attributes (i.e., object properties) are used
- Specify enumerations of the attributes (i.e., properties)
- Variety of other quality-control constraints
- Presentational versus conceptual encoding
- MathML has a presentational mode - i.e., markup used to control how formulae are displayed on-screen
- MathML also has a content encoding mode - useful for when the markup will be processed by calculator-type programs
- General
- Documents Describing Documents
- General
- Not all documents have the capability to describe themselves (i.e., to include metadata)
- Sound and graphic files are notable examples of this
- Ergo, we now have (XML) documents whose purpose is to describe other documents
- Describing media - esp. RSS
- Templates - i.e., XSLT
- General
- Simple Data Storage
- Document/Data Integrity
- Well-Formedness (i.e., verification that proper XML syntax has been adhered to)
- Proper nesting, matching opening & closing tags, etc.
- Generally, that markup adheres to rules discussed in XML Object Types: Element (2 types) - incl. Attributes & Namespaces
- Validity (incl. schemas & DTDs)
- General
- A schema is "a generic representation of a class of things" (Ray, p. 108)
- A schema is a type of pass/fail test for a document or for one of its parts
- A schema defines what 'kind' of document an XML document is
- Schema tells a program how to read an XML document
- Schema provides a way to validate a document
- Other terminology is that an XML document conforms to a schema
- Validating parsers
- Built by processors after reading a schema
- Validating parser produces a validation report as output after reading appropriate XML document
- At a minimum the return code is true or false, based on whether the document is a valid instance
- Can also produce a PSVI - this provides info on data types and structures
- 4 levels of validation
- Structure
- Syntactically correct markup
- Most important level
- Data typing
- Relates to patterns of character data
- Not widely supported
- Integrity (i.e., that links between nodes and resources work)
- Business rules (i.e., misc. rules such as correct spelling, arithmetic constraints, etc.)
- Structure
- Using DTDs & Schemas
- Advantages
- Provides a publishable specification
- Human-readable
- Catches higher-level mistakes (e.g. misspelled element names, missing attributes, etc.) than does a simple well-formedness check
- Portable (i.e., can be used across documents)
- Typically more efficient than writing a program to test a document
- Extensible
- Drawbacks
- Reduces flexibility
- Advantages
- DTDs
- General
- Derive from SGML (and actually pre-date XML)
- Most widely supported schema language
- Mostly markup-focused
- DTDs are not themselves XML documents
- DTDs are saved with a
.dtd
file extension - Advantages
- Included in the core XML specification
- Useful for entities
- Useful where documents will be human- rather than program-generated
- Limitations
- DTDs do not support namespaces (namespaces appeared after the original XML specification)
- Weak data-typing
- Do not work well with models where there are required child elements but the 'children' can appear in any order
- Typically slows down the authoring process
- DTDs and schemas require maintenance
- May require their own documentation
- Over time problems with version rot arise
- Schemas are ontologies and can be intellectually difficult to construct correctly
- Methodology
- Declares a set of allowable elements
- i.e., defines a document "vocabulary"
- Only elements specified within the DTD may appear in the document
- Defines a content model for a document
- i.e., defines a document"grammar"
- Specifies
- What content and/or sub-elements can appear within elements
- The order of element contents
- Allowable number of element contents (one, one or more, etc.)
- Whether certain content is required or optional
- Defines allowable attributes for each element
- Specifies attribute names
- Specifies default values (if any)
- Specifies behavior - i.e., required or optional attributes
- Misc. ease-of-use mechanisms
- Parameter entities can be specified
- Portions of the model can be imported from external sources
- Declares a set of allowable elements
- Syntax - general
- Document prolog
- White space can be placed anywhere except in the initial declaration type
- Declarations
- Declarations are the "rules" of a DTD
- Each declaration adds (to the language being defined) either:
- New element
- Set of attributes
- Entity
- Notation
- Where redundancy/conflicts arise the first declaration takes precedence
- Parameter entities
- These are entity declarations which exist inside a DTD
- Using parameter entities to build a DTD is called modularization
- Must be declared before being referenced
- DTDs do not specify a root element
- This poses some obvious problems
- On the other hand, allows DTDs to be combined rather easily
- Syntax - specific
- To indicate a list of optional elements or attributes
- Enclose element or attribute names within parentheses
- Separate optional element or attribute names with vertical bars (|)
- A question mark (?) following the parenthetical list means that none of the options even needs be used
- Elements
- Simple element name:
<!ELEMENT elementname>
- Element and required (one each) sub-elements:
<!ELEMENT elementname (sub-element1, sub-element2, …)>
<!ELEMENT sub-element1>
<!ELEMENT sub-element2>
… - Note 1: Elements & their sub-elements must appear in the order in which they are declared
- Cardinality (i.e., the number of times an element can or must occur)
- Once but only once - the default
- Once or more - place a plus-sign (
+
) following the name of the element - Zero or more (i.e., any number of times) - place a asterisk (
*
) following the name of the element - Optional - place a question mark (
?
) following the name of the element
- Simple element name:
- Element content keywords
- Options:
#PCDATA
- character data- This means the element takes zero or more characters
- Also means the element contains character data as opposed to containing another element
- For better and for worse, DTDs never check the actual details of character data used in a document
EMPTY
ANY
(any combination of parsable data)
- Content keywords follow the name of the element in the DTD declaration
- Example:
<!ELEMENT lastname #PCDATA>
- Mixed content - i.e., an element can contain other elements or character data
- Syntax:
<!ELEMENT mixedContentElemName (#PCDATA | sub-element1 | sub-element2 | …)*>
- Among the optional elements
#PCDATA
must be listed first - Asterisk (*) is required - it indicates zero or more of whatever came before
- Cannot replace asterisk(*) with a plus (+) to require that an element with mixed content contain character data
- Syntax:
- Options:
- Attributes
- Basic syntax (to define the attribute(s) of a given element):
<!ATTLIST elementname
attributename1 attributeTypeKeyword requirementKeyword
attributename2 attributeTypeKeyword requirementKeyword
… - Attribute type keywords
CDATA
- character dataID
- unique ID- Parsers will check for uniqueness, but only within a given document
- Uniqueness will be enforced across all element names
- As with XML names, an ID value may only begin with a letter or underscore
IDREF, IDREFS
- the ID(s) of (an)other element(s)NMTOKEN, NMTOKENS
- (a) valid XML name(s)ENTITY, ENTITIES
NOTATION
xml:
- a predefined xml value
- Attribute requirement keywords
#REQUIRED
#IMPLIED
- Means that there is an enumerated list of usable/valid values
- The parenthetical, vertical-bar-delimited set of allowable values must immediately precede the
#IMPLIED
keyword - Note 1: when working with prefixed attributes (e.g.,
XLINK-
) where the namespace defines available choices, use#IMPLIED
as a stand-alone keyword - Note 2: means the attribute is not required
#FIXED
#FIXED
keyword followed by some value delimited in quotes- Attribute must/can only take the value inside the quotation marks
- Basic syntax (to define the attribute(s) of a given element):
- To indicate a list of optional elements or attributes
- Parameter entities
- General
- Note: parameter entities apply only in DTDs
- Represent/hold recurring parts of declarations
- In external declarations can be used to hold
- Element groups
- Content models
- Attribute definitions for attribute list declarations
- In internal subset can only hold complete declarations, not fragments
- Syntax
- They are preceded by a percentage sign (%) both when declared and referenced
- Example (declaration):
<!ENTITY % common.atts "
id ID #IMPLIED
class CDATA #IMPLIED"
> - Example (parameter entity reference):
<!ATTLIST someElement %common.atts;>
<!ATTLIST anotherElement %common.atts;
newAttribute CDATA #IMPLIED "xyz"
>
- General
- DTD Design Principles
- Organization
- With larger DTDs
- Break up into separate modules - i.e., files
- Modules are saved with a
.mod
file extension - Note: modules can be "turned off" for debugging purposes
- Group declarations by function
- By block & inline
- By hierarchical elements
- By tables, lists, etc.
- With larger DTDs
- Whitespace (use liberally)
- Comments (use liberally)
- Version tracking - use!!
- Attributes versus elements
- Keep elements specific
- Use a hierarchical structure
- Elements should hold content that is part of the document
- Attributes should modify the behavior of an element
- Modularization (2 methods)
- Import modules from external sources
- Drawbacks
- Referencing an external parameter entity incorporates the entire file
- There is no concept of scope when it comes to DTDs - i.e., local declarations do not override declarations in an externally references parameter entity
- Drawback workarounds
- In the internal subset "predeclare" an entity to override it (1st declaration takes precedence)
- This works because the internal subset is read before external subset, & 1st declarations take precedence
- This only works for attribute declarations, however; it is a validity error to declare an element more than once
- Use conditional sections to override an external element declaration
- Drawbacks
- Conditional sections
- Can only be used with external subsets
- Syntax is exactly like that of CDATA sections
- Two keywords are INCLUDE and IGNORE (used in place of the CDATA keyword)
- The 'trick' to conditional sections is to set the INCLUDE/IGNORE keyword with a parameter entity
- Example:
<!ENTITY % optionalDeclarations "INCLUDE"> <![%optionalDeclarations;[
<!ELEMENT parental (kid1, kid2, kid3?)>
<!ATTLIST parental
attrib1 CDATA #IMPLIED>
…
]]>
- Using the internal subset
- Import modules from external sources
- Organization
- General
- Schemas (briefly…)
- Can handle element (i.e., text) content, which DTDs essentially do not
- Also, a schema is itself an XML document, unlike a DTD
- Notable schemas
- RELAX NG
- Combination of RELAX and TREX
- Supports namespaces and datatypes
- Schematron
- RELAX NG
- See XML Schemas - Structures and XML Schemas - Datatypes
- General
- Well-Formedness (i.e., verification that proper XML syntax has been adhered to)
- Presentation & Transformation
- Presentation Part I: CSS (briefly…)
- General
- Use of CSS limited to cases where output text will be in the same order as input data
- CSS invoked via a "processing instruction"
- Stylesheets
- A stylesheet which generates display is a client-side processor
- A stylesheet can also generate another document
- This, of course, is exactly what XSLT does
- Such a transformation can take place on either server or client side
- Associating a stylesheet to a document
- Technique varies language by language
- Some XML languages provide their own presentation software
- Others may simply not support CSS stylesheets
- Generic markup:
<?xml-stylesheet
type="MIME-type"
href="url-of-stylesheet"
?> - Note: if/when using CSS stylesheets the
type
attribute of the processing instruction should be set to"text/css"
- Technique varies language by language
- Limitations
- You only get one pass through document with a style sheet
- I.e., cannot look forward or backward in a document to decide on styling
- When you want to change the order in which elements are rendered you must move to something more powerful (e.g., XLST)
- See CSS 2.1
- General
- XPath and XPointer
- General
- XPath = XML Path Language
- XPointer = XML Pointer Language
- Nodes & trees
- General
- "…there is only one possible tree configuration for any given document…" (Ray, p. 100)
- Ergo, there is a unique path from document root to any point inside document
- XPath describes that route
- Node types
- Root
- Special kind of node - it is not an element!
- Contains:
- Document element
- Any comments &/or processing instructions surrounding document elemtn
- Element
- Root and elements are the only node which can contain other nodes
- Element can contain any other type of node except the root node
- An empty element is called a leaf node
- Attribute
- Treating as a node allows you to access an element's attribute(s)
- Like an element node which contains only text
- Text
- When uninterrupted treated as a leaf node
- Always the child of an element node
- Note: an element can contain more than one text node; text can be broken up by elements or other node types
- Comment
- Processing instruction
- Namespace
- Note that a namespace declaration is not treated as simply another attribute node
- Because namespace declarations affect all child elements, a namespace is a region of a document, not of an element
- Notes on DTDs:
- DTDs are not treated as node types!
- XPath assumes all entity references are resolved prior to XPath looking in document
- This approach is required since entities might resolve to XML mark-up
- Root
- Trees & subtrees
- Think of document as collection of subtrees
- Note: a node tree must contain all appropriate opening & closing XML tags
- General
- Finding nodes
- General
- A location path represents a chain of location steps which allow you to move around in a document
- 3 parts of a location step
- axis - the direction to travel
- node test - specifies what kind of nodes to travel through
- predicates - optional boolean tests
- Context node = 'current' node
- Node axes
- Keywords
Ancestor
- All ancestors of context nodeAncestor-or-self
Attribute
- attributes of the context nodeChild
- children of the context nodeDescendent
- all generationsDescendent-or-self
Following
- Nodes that follow at any level
- Does not include context node's own descendents
- Does include descendents of other (following) nodes
Following-sibling
- Like
following
, but does not include any descendant nodes - Only includes sibling-level (i.e., having same parent) nodes
- Like
Namespace
- all the namespace nodes of an elementParent
Preceding
- Does not include descendents of the context node
- Does include prior siblings and their descendents
Preceding-sibling
- no descendentsSelf
- i.e., the context node
- Syntax
axisname::nodetest[predicate]
- More often than not…
- Simply use the name of a node
- Node type is then inferred from the axis
- Within the attribute axis node is assumed to be an attribute
- Within the namespace axis node is assumed to be a namespace
- In all other cases node is assumed to be an element
- "In the absence of a node axis specifier, the axis is assumed to be
child
and the node is assumed to be of type element." [Emphasis mine - WHW] (Ray, p. 100)
- Keywords
- Node tests
/
- Again, not synonymous with the root element
- Contains the root element plus all comments or processing instructions preceding root element
node()
- Matches any node
- Example:
attribute::node()
selects all attributes of the context node
*
- In the attribute axis any attribute
- In the namespace axis any namespace
- In all other axes any element
foo
- In the attribute axis the
foo
attribute of the context node - In the namespace axis the
foo
namespace - In all other cases the element
foo
- In the attribute axis the
text()
- any text nodeprocessing-instruction()
- any processing instructionprocessing-instruction('for-web')
- any processing instruction with afor-web
targetcomment()
- any comment node
- Location path shortcuts
//
- Starting at the root node - i.e., anywhere in the document
//foo
≡ anyfoo
element anywhere in the document
.//
- Selects all matching nodes which are descendents of context node, regardless of how many generations below
- ≡
/descendent-or-self::node()//
.
- the current (aka context) node, ≡self::node()
..
- parent of the current node@
- Use to select attributes
- E.g.,
@foo
≡attribute::foo
/*
- the document element- Remember that
/
is an absolute path & matches the root node *
then matches any element, or, in this case the document element
- Remember that
../*
- Matches all sibling elements
- Also includes current element if context node is an element
¦
- use to join together paths as an AND statement
- Predicates
- A boolean expression enclosed within square brackets (i.e.,
[ ]
) - See W3.org's tutorial for examples of how to use predicates
- A boolean expression enclosed within square brackets (i.e.,
- General
- XPath Expressions
- General
- Node set expressions
- Numeric expressions
- String expressions
- XPointer
- General
- Syntax
- General
- Schemes & chained xpointers
- XLink
- Shorthand pointers
- Points
- Character escaping
- XPointer functions
- Constructing ranges
- Ranges from points & nodes
- Ranges from strings
- Finding range endpoints
- Returning points from documents
- General
- Transformations with XSLT
- General
- History
- Concepts
- Running transformations
- The stylesheet Element
- Templates
- Formatting
- Presentation Part II: XSL-FO
- Presentation Part I: CSS (briefly…)
- Bibliography
-
"Extensible Markup Language (XML) 1.0.",
T. Bray, J. Paoli, C. M. Sperberg-McQueen, E. Maler, F. Yergeau,
Editors. World Wide Web Consortium (W3C),
26 November 2008. This version of the XML 1.0 Recommendation is available at
http://www.w3.org/TR/2008/REC-rdfa-syntax-20081014/
. The latest version of this Recommendation is always available athttp://www.w3.org/TR/xml/
. - Goldberg, Kevin Howard. Visual QuickStart Guide: XML. 2nd Ed. United States of America: Peachpit Press, 2009. Print.
- Ray, Erik T.. Learning XML. 2nd Ed. United States of America: O'Reilly, 2003. Print.
- Walmsley, Priscilla. Definitive XML Schema Upper Saddle River, NJ 07458: Prentice Hall PTR, 2002. Print.
- Watt, Andrew H.. Sams Teach Yourself XML in 10 Minutes United States of America: Sams Publishing, 2003. Print.
-
"Extensible Markup Language (XML) 1.0.",
T. Bray, J. Paoli, C. M. Sperberg-McQueen, E. Maler, F. Yergeau,
Editors. World Wide Web Consortium (W3C),
26 November 2008. This version of the XML 1.0 Recommendation is available at
- XML Names
- XML Schema 1.0
- The Basics
- Overview
- The components of XML Schema
- Delineated
- Element
- Attribute
- Simple type
- Complex type
- Notation
- Named model group
- Attribute group
- Identity constraint
- Declarations vs. definitions
- A declaration is used for a component that
- Can appear in an instance (i.e., an XML document)
- Can be validated by name
- A definition refers to components that are internal to the schema, e.g.:
- Data types
- Model groups
- Attribute groups
- Order of appearance of declarations & definitions is immaterial
- A declaration is used for a component that
- Global vs. local components
- Global types
- Two essential characteristics
- Appear at the top of a schema document
- Are named
- Further, names of global types must be unique within the entire schema
- Two essential characteristics
- Local types
- Scoped to their parent definition or declaration
- Complex types can contain local elements (either simple or complex) & attribute declarations
- Global types
- Sample Mark-up:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/SMLSchema">
<xsd:element name="hsStudent" type="HSStudentType" />
<xsd:complexType name="HSStudentType">
<xsd:sequence>
<xsd:element name="studentNm" type="xsd:string" />
<xsd:element name="hsYear" type="HSYrType" />
</xsd:sequence>
<xsd:attribute name="birthDt" type="xsd:date" />
</xsd:complexType>
<xsd:simpleType name="HSYrType" />
<xsd:restriction base="xsd:integer">
<xsd:minInclusive value="9" />
<xsd:maxInclusive value="12" />
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
- Delineated
- Data types
- Simple vs. complex types
- Simple types have neither
- Attributes
- Child elements
- Complex types have either or both
- Attributes are always simple types
- Simple types have neither
- Named vs. anonymous types
- Named types
- Defined globally, at top of document
- Must have a unique name within the schema
- Anonymous types
- Cannot have names
- Are locally scoped
- May only be used once by their parent
- Named types
- The type definition hierarchy
- One type can derive from another
- Derived type can either expand or restrict parent definition
- Derived type inherits all of its parent's characteristics
- Simple vs. complex types
- Simple types
- Built-in simple types
- There are 44
- Categories
- Strings & names
- Numeric
- Date & time
- Legacy types
- Other/misc.
- Simple types can be restricted, using facets
- List & union types
- Most simple types are atomic types - i.e., they are indivisible
- However, can also have simple types which are lists or unions
- Built-in simple types
- Complex types
- Content
- Content is what appears between an element's opening an closing tag
- Content is either/or:
- Character data
- Child elements
- Attributes are not content
- Content types
- Simple - character data only
- Element - elements only, but no character data
- Mixed - both character & element data
- Empty
- Content models
- "The order and structure of the child elements of a complex type are known as its content model." (Walmsley, p. 26)
- Components of a content model
- Model groups
- Element declarations or references
- Wildcards
- Model group
- Types
sequence
groups - dictates the order of child elementschoice
groupsall
groups- Child elements appear 0 or 1 time
- Order is immaterial
- Can be nested
- Can occur multiple times
- The
any
element- Known as a wildcard
- Allows for open content models
- Types
- Content
- The components of XML Schema
- Namespaces
- Namespaces in XML
- Namespace names are URIs
- URIs can be
- URLs
- URNs
- Purpose of a namespace
- To provide a unique name
- Is not to provide a dereferencable resource
- Relative references (e.g.,
../schema
)- While legal, URIs relative references are not appropriate in namespace declarations
- Namespace declarations must be unique, and relative references may not be unique
- Namespaces are case-sensitive
- URIs can be
- Namespace declarations & prefixes - see discussion on XML Namespaces
- Default namespace declarations - see discussion on XML Namespaces
- Name terminology
- Qualified names (aka QNames)
- Means that the name is qualified by a namespace
- Two possible syntaxes for this
- Name contains a mapped prefix
- Name contains no prefix but there is a default namespace declaration
- Unqualified names
- For elements: there is no default namespace & the element has no prefix
- For attributes: there is no prefix (as will be discussed below, un-prefixed attributes are not mapped to a/the default namespace)
- Prefixed names
- Name uses a prefix
- Prefixed names are 'always' qualified names (unless the prefix is out of scope)
- Unprefixed names
- Local names
- The part of a qualified name that is not the prefix
- Example:
author
inbibo:author
- Non-colonized names (aka NCNames)
- XML names without colons
- All prefixes, local names, & unprefixed names are NCNames
- Qualified names (aka QNames)
- Scope of namespace declarations
- Applies to children, grandkids, etc.
- Standard Practice: place namespace declarations in your root element's start tag
- Overriding namespace declarations (can be done)
- Attributes & namespaces
- "Attributes with prefixed names are also known as global attributes." (Walmsley, p. 45)
- Un-prefixed attribute names
- Attributes are not affected or governed by default namespaces
- Therefore, un-prefixed attributes are technically not in any namespace
- (It can be argued that un-prefixed attributes are in the namespace of the containing element)
- Namespace names are URIs
- Relationship between namespaces & schemas
- Many-to-many relationship
- A namespace can be defined by 0+ schemas
- Multiple schemas with overlapping declarations within a single namespace
- In & of itself this is perfectly legit
- The restriction is that a single instance cannot access these overlapping schemas simultaneously (at least it certainly cannot incorporate a declaration that can be found in more than 1 referenced schema)
- A single schema can declare names for 0+ namespaces
- Using namespaces in XSDL
- Target namespaces
- There can be only 1 target namespace defined by a schema
- Every global declaration in a schema is associated with that target namespace
- Syntax/Example:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://rcpconsulting.biz/schemas"
targetNamespace="http://rcpconsulting.biz/schemas">
<!-- schema definition, etc. -->
</xsd:schema> - Local element declarations do not have to refer to the target namespace
- Note: if you are not going to employ namespaces in a schema document there is really no need to declare a target namespace
- The XML Schema namespace
- Use this namespace to create custom schema
- When doing so you never prefix XSD attributes because none of them is global
- The XML Schema Instance namespace
- Typically prefixed with
xsi
- Defines four schema-related attributes:
type
nil
schemaLocation
noNamespaceSchemaLocation
- See Using the instance attributes (below)
- Typically prefixed with
- Namespace declarations in schema documents
- At a minimum, you must declare the XML Schema Namespace in a schema document
- Of course, can also declare a target namespace
- 3 options for handling namespace declarations in a schema document
- Mapping a prefix to the XML Schema namespace
- Again, typically use
xsd
orxs
- Walmsley tends to prefer this approach
- If you do not declare a target namespace
- You must declare a prefix for the XML Schema Namespace in a schema document
- Otherwise you cannot reference components not in the namespace
- Again, typically use
- Mapping a prefix to the target namespace
- This is equivalent to making XML Schema the default namespace
- Note: You may have to map a prefix to your target namespace if you are going to use identity constraints
- Mapping a prefix to all namespaces
- Mapping a prefix to the XML Schema namespace
- Target namespaces
- Namespaces in XML
- Schema Composition
- General
- Schemas are not necessarily synonymous with schema documents
- Schemas can be composed of multiple documents
- Modularizing schema documents
- With larger, more complex schemas it can often be advantageous to break things down into separate schema docs
- If doing this, probably want to think about component objects (as if you were doing OOP)
- Advantages to breaking up a schema into separate documents
- Easier re-use
- Ease of maintenance
- Reduced chance of name collisions (via multiple namespaces)
- Access control
- Approaches for dividing a schema into separate documents
- Subject area
- If the schema is to be used across various applications might write one schema per app or per database
- With textual instances you might have one schema for each type of doc
- General/specific
- Might create a schema for elements that will be used most widely
- Then create individual schemas for more specific circumstances
- Basic/advanced
- Subject area
- Defining schema documents
- General
- A schema "document" need not even be a document
- Schema can be a fragment inside another XML doc, in memory, etc.
- "Each schema document describes components for at most one namespace, known as its target namespace." (Walmsley, p. 59)
- Defining a schema document in XSDL
- Root element of each schema document is the
schema
element - Attributes (none required)
id
version
xml:lang
targetNamespace
attributeFormDefault
&elementFormDefault
- Sets whether local attribute & element declarations should use qualified names
- Enumerated choices
"qualified"
"unqualified"
- Default: "unqualified"
blockDefault
- Sets whether to block element or type substitution
- Enumerated choices
"#all"
or- One of
"substitution", "extension", "restriction"
finalDefault
- Sets whether to block type derivation
- Enumerated choices
"#all"
or- One of
"extension", "restriction", "list", "union"
- Content
- One of
include, import, redefine, annotation
(1+ times) - One of
attribute, attributeGroup, complexType, element, group, notation, simpleType
(1+ times) annotation
(1+ times)
- One of
- Root element of each schema document is the
- General
- Schema assembly
- Assembling schemas from multiple documents
- Typically there is a main schema document which includes or imports other schema docs
- Alternative approaches:
- Simply let the relevant instance document identify multiple schemas and/or schema locations
- Let processor assemble schema documents from predefined locations
- Employ command line parameters to pull in various schema docs
- Uniqueness of qualified names
- Globally declared components must have unique qualified names in a schema (i.e., being unique in a particular schema document is not sufficient)
- This especially becomes an issue when one schema document
include
s another (because the namespaces will be the same) - The issue of duplications covers all global components, whether attributes, simple & complex types, & more
- An 'exception' to this rule:
- The concern about uniqueness applies only to the same type of components
- That said, simple & complex types cannot have the same qualified name
- Missing components
- Watch for references to components where you have not incorporated the containing schema document
- Whether this generates an error will depend on whether or not the component is involved in the document's validation
- Schema document defaults
- There are 4 defaults among XSDL attributes
attributeFormDefault
elementFormDefault
blockDefault
finalDefault
- Default settings are not overridden when schema documents are incorporated into schemas
- There are 4 defaults among XSDL attributes
- Assembling schemas from multiple documents
include, redefine & import
include
- Used only for schemas within the same namespace
- Attributes
id
schemaLocation
- Required
- Type is a URI
- Does not have to be resolvable
- If it is resolvable it must refer to a complete schema document
- Syntax/Example:
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.rcpconsulting.biz/schema"
targetNamespace=
"http://www.rcpconsulting.biz/schema">
<xsd:include schemaLocation="someOtherSchema.xsd">
<!-- schema details, etc… -->
</xsd:schema>
include
location- Can only be in the top level of a schema document
- Must appear at the beginning (as is the case with
import
&redefine
)
- When using an
include
1 of the following must hold:- Container & contained schema have same target namespace
- Neither schema document has a target namespace
- Container schema document has a target namespace while included schema document does not
- "…all components of the included schema document take on the namespace of the including schema document." (Walmsley, p. 67)
- The included components are called chameleon components, because their operative namespace depends on the document into which their own document is included
- Can employ multiple
include
s in a schema document - Can also have "nested"
include
s
redefine
- Similar to
include
, yet allows you to, well, redefine select components in the included schema document - Components which can be redefined:
- Complex types
- Simple types
- Named model groups
- Attribute groups
- See Redefining Schema Components
- Similar to
import
- Used when referring to components from other namespaces
- Difference vs.
include
include
is typically used to pull together schema documents that were designed to be used in tandemImport
is used to "record a dependency on another namespace, not necessarily another schema document." (Walmsley, p. 70)
- Attributes (all optional)
id
namespace
- The namespace being imported
- Imported namespace cannot be the same as the importing namespace
- Do not specify this if the components being imported are not in a namespace
- If, however, the importing schema document itself has no target namespace then it can only import schema documents which themselves have a
namespace
attribute
schemaLocation
- As with
include
, if you include this attribute it must be a resolvable reference - On the other hand the processor is not required to attempt to resolve it
- As with
- You can have multiple imports of the same namespace
- Looping references among imported schema documents
- This is actually permissible
- Simply indicates interdependence
- General
- Instances & Schemas
- Using the instance attributes (i.e., those defined by XML Schema Instance Namespace)
- The 4 attributes (all are optional)
nil
- Boolean
- Default:
false
- Sets whether the element's value =
nil
- Note: to use this attribute the element itself must be nillable
- See Nils & nillability (below)
type
- Takes a
QName
- Use to override element's declared type
- Takes a
schemaLocation
- Can specify a whitespace delineated list of URIs
- See The
xsi:schemaLocation
attribute (below)
noNamespaceSchemaLocation
- Specify a single URI
- See below
- Working with the XML Schema Instance namespace
- You must declare the namespace in your instance
- You must also map a prefix to the namespace (typically
xsi
) - However, you do not need to specify a location, as all processors recognize the schema
- You must also map a prefix to the namespace (typically
- XSI attributes are declared globally so they must be prefixed
- Again, using any of the XSI attributes can be done inside a simple type, without changing matters into a complex type
- You must declare the namespace in your instance
- The 4 attributes (all are optional)
- Schema processing
- Validation
- Integral part of schema processing
- Validation process checks
- Correctness of the data
- Certainly not infallible
- Does, however, catch incorrect data types & entries that are out of range
- Completeness of the data
- Correctness of the data
- Walmsley indicates ( p. 78) that you can control how often data is validated by a schema
- Augmenting the instance
- A schema processor itself may alter the data in an instance
- Specifically, a processor can:
- Add default and/or fixed values, whether for content or for attribute values
- Normalize "whitespace in element and attribute values that have simple types." (Walmsley, p. 78)
- Validation
- 'Finding' schemas
- Options for relating instances to schemas
- Provide hints to the processor from within the instance, via:
xsi:schemaLocation
xsi:noNamespaceSchemaLocation
- Application's choice
- Apps tend to process the same schema repeatedly
- Processors & apps also often have many schema locations "built in"
- When a processor or app thinks it knows location of a schema the app may:
- Ignore
xsi:schemaLocation
hints - Flatly reject documents containing an
xsi:schemaLocation
attribute - Reject such documents if the hinted-at schema is not what the processor expects to find
- Ignore
- User's choice
- Set via some dialog
- Set via command-line directives
- Dereferencing the namespace
- Provide hints to the processor from within the instance, via:
- Using XSDL hints in the instance
- The
xsi:schemaLocation
attribute- You "specify a list of pairs that match namespaces with schema locations." (Walmsley, p. 80)
- First value is the namespace name
- Second value
- URL for the schema location
- At a minimum this URL must contain the file name of the schema
- Once the schema file is retrieved processor first makes sure it is target namespace matches the namespace specified in
xsi:schemaLocation
- Can by all means include multiple namespace/location pairs in the attribute's value
- Master schema
- Best Practice: use when working with multiple schema
- Helps make name collisions more obvious
- If master schema provides a way to find its imported schema there is no need to list the imported schema in
xsi:schemaLocation
attribute
- Attribute location
- Can appear anywhere in instance
- Can be used multiple times
- Location does not signify scope - however, it must precede any element it validates
- Certain processors may ignore the attribute if it is not in the root tag
- You "specify a list of pairs that match namespaces with schema locations." (Walmsley, p. 80)
- The
xsi:noNamespaceSchemaLocation
attribute- Only takes a single value - the URI of the schema
- Technically legal to use this attribute along with
xsi:schemaLocation
attribute
- The
- Dereferencing namespaces
- Dereferencing = making your namespace the actual URL where your schema can be found
- Under no obligation whatsoever, however, to use dereferencing
- Reasons not to require an app to deference a namespace name
- Security
- Performance
- Network availability
- If you do want app to dereference a namespace name it's still better not to locate schema directly there:
- You may have many schemas comprising the namespace
- You may also have other documents (DTDs, stylesheets, etc.) as part of the namespace
- Schemas aren't very readable
- Best Practice: place a resource directory at namespace URL
- Can include both human- & app-readable resources there
- Walmsley (p. 84) recommends using Resource Directory Description Language (aka RDDL) as language to handle this
- RDDL is an XHTML extension
- Main element is
resource
resource
attributes are:role
- the nature of the resource (e.g., DTD, schema,…)arcrole
- the purpose of the resource (e.g., validation, reference,…)
- See Walmsley, Example 5-5, pp. 85-6, for a good example of XHTML with RDDL mark-up
- Note: using RDDL entails the use of XLINK
- Options for relating instances to schemas
- The root element
- Any global element declaration can be a root element
- As with DTDs there is no way to designate a root element (other than by declaring only 1 global element)
- If using substituion groups or imports having more than 1 global element may be unavoidable
- Sometimes you simply have to rely on your app to verify the desired root element
- Using DTDs & schemas together
- Can certainly validate against both
- Validation process
- DTDs are validated first
- As DTDs are validated the instance may be populated with default values, normalizing whitespace, etc.
- Schema validation then takes place against the altered value of the instance
- DTD declarations do not override schema constraints
- Specific schema processors (as of 2002)
- XSV
- Xerces
- Oracle XDK
- Microsoft MSXML
- Using the instance attributes (i.e., those defined by XML Schema Instance Namespace)
- Schema Documentation & Extension
- The mechanics (2 methods) of extending XSDL
annotation
element- General
- Can appear in any XSDL element except:
- Another
annotation
element - Its own child elements:
documentation
appinfo
- Another
- Frequency & placement restrictions
- Unlimited within
schema
&redefine
elements - All other elements:
- Can only appear once
- Must be the first child
- Unlimited within
- Syntax
- Attribute (only 1):
id
- Elements:
documentation
- used for human-readable contentappinfo
- machine-readible content
- A single
annotation
element can contain multipledocumentation
&appinfo
children, & they can appear in any order
- Attribute (only 1):
- Can appear in any XSDL element except:
- User documentation (i.e., the
documentation
element)- Attributes
source
- Takes
anyURI
as its type - Use to link to source of further documentation
- Takes
xml:lang
- takes alanguage
value (e.g., "en")
- Content - mixed, with
any
wildcard (see below)
- Attributes
- Application information (i.e., the
appinfo
element)- Syntax: same as for
documentation
except that there is noxml:lang
attribute - Best Practices (for both
documentation & appinfo
)- Use child elements instead of character data content
- Use links to external documents, via
source
attribute or an XLINK attribute, to promote reuse of documentation
- Syntax: same as for
- Validating annotations
- Wildcard content is handled via lax validation
- Parser will validate those elements for which it can find schemas/declarations
- If no such declarations can even be found no errors are reported at all
- Because of lax validation you can "make up" enclosed elements & not bother to create a supporting schema
- To enforce stricter validation
- Use the
xsi:schemaLocation
attribute - Note: The value supplied is only a hint; not all processors check on the value supplied
- If you do compile a schema for your documentation…
- Do not declare a
documentation
element - Instead, create global declarations for the elements you want to pop into your documentation
- Do not declare a
- Use the
- Wildcard content is handled via lax validation
- General
- Non-native attributes
- Example:
<xsd:schema xmlns:xsd="http://…"
xmlns:doc="http://www.rcpconsulting.biz/schemas"
<xsd:element name="widget" type="widgetType"
doc:description="A much, much better mousetrap" />
<!-- … -->
</xsd:schema> - Validation - wildcard with lax validation
- Example:
- Using annotations vs. non-native attributes
- Similar to issue of when to use elements vs. attributes
- Non-native attribute pros:
- Less verbose
- Often more clear, because typically more closely tailored to instance
- Non-native attribute cons:
- Cannot be used more than once per element
- Cannot be extended down the road
- Connot contain other elements
- Restricted to text (e.g., cannot use XHTML)
- User documentation
- Types/examples
- Descriptive text (of course)
- Explanation of structure of your schema
- Meta data (e.g., copyright, author, version)
- Examples
- Data element definitions
- Especially important when you are creating a schema you intend to be reused
- Documentation elements to consider using (for each declared element!)
- Title
- Identifier
- Version
- Definition (text content)
- Keyword (allow multiple uses)
- See Walmsley, Example 6-8, p. 107, for a semi-real world example
- Code documentation (suggestions)
- Date created
- Author
- Dependencies
- Version
- Deprecated
- Section comments
schema
&redefine
elements are allowed to have multipleannotation
elements so…- You can break up more complex documents into sections
- You can apply section-specific documentation
- See Walmsley, Example 6-9, p. 107, for an example
- Types/examples
- Application information
- Types of application information
- Extra validation rules, such as co-concurrence (which XSDL cannot handle)
- Mapping to other structures (typically databases)
- Mapping to XHTML, etc.
- Formatting information
- Schematron for co-occurrence constraints
- Schema Adjunct Framework for RDBMS mappings
- "Schema adjuncts are XML documents that can be used to provide additional information about schemas, such as… database mappings…" (Walmsley, p. 111)
- The Schema Adjunct Framework is a specification for schema adjuncts
- Can be a separate document
- Can be embedded in the schema itself
- For sample mark-up see Walmsley, Example 6-13, p. 112
- Info on Schema Adjunct Framework
- Types of application information
notation
element- General
- Used to show the format of non-XML data
- Example: use
notation
to flag whether a binary in apicture
element is JPEG or GIF - Can be used on:
- Embedded data (i.e., data in an XML instance itself)
- Files linked to the instance via unparsed entities
- Notations can take a
system
orpublic
identifier - However, no standard names or identifiers exist for file formats like JPEG
- Options
- Point to an app that can process the format
- Provide documentation about the format
- Provide an abbreviation that an application will recognize
- Note: schema processors do not attempt to resolve any identifiers
- Typical example: specifying a
fmt
attribute to be used inside apicture
element
- Declaring a notation
- Attributes (type)
id
(ID
)name
(NCName)
- requiredpublic
(token
) - public identifiersystem
(anyURI
) - system identifier
- Content - only
annotation
is allowed - Notations are always declared globally!
- Qualified name must be unique among all notations in its schema
- As with all named, global components, notations take on target namespace of the schema doc
- Syntax/example:
<xsd:schema xmlns:xsd="http://www.w3.org/…">
<xsd:notation name="jpeg" public="JPEG" />
<xsd:notation name="gif" public="GIF" />
<!-- … -->
<xsd:schema />
- Attributes (type)
- Declaring a notation attribute
- Declare an attribute whose type has a
restriction
element base
attribute of thatrestriction
element has a value"xsd:NOTATION"
- The
restriction
element containsxsd:enumeration
elements - The
value
attribute of eachxsd:enumeration
element refers to thename
of a globally-declaredxsd:notation
element
- The
- Note: Values of
"xsd:NOTATION"
-based attributes are qualified names. I.e., must either be:- Prefixed
- In the scope of the default namespace declaration
- Declare an attribute whose type has a
- Notations & unparsed entities
- Notations can be used to identify format of an unparsed general entity (e.g., graphics data embedded directly in XML instance)
- See Walmsley, Example 6016, p. 115
- General
- The mechanics (2 methods) of extending XSDL
- Overview
- Schema Components - Basic
- Element Declarations
- Global & local element declarations
- Global element declarations
- These must be directect children of
schema
element - As such, they appear at the top of the schema doc
- Attributes (only
name
is a required attribute) & attribute typeid
(ID
)name
(NCName
)type
(QName
) - to specify data typedefault
(string
) - default valuefixed
(string
) - fixed valuenillible
(boolean
)- Controls whether
xsi:nil
can be used in the instance - Default =
"false"
- Controls whether
abstract
(boolean
) - default ="false"
substituionGroup
(QName
)block
- allowable values:"#all"
, or:- List of:
"substituion", "extension"
, or"restriction"
- Defaults to
blockDefault
ofschema
final
- allowable values:"#all"
, or:- List of:
"extension"
or"restriction"
- Defaults to
finalDefault
ofschema
- Allowable content
annotation?
(simpleType ¦ complexType)?
(key ¦ keyref ¦ unique)*
- Element constraints - i.e.,
minOccurs
&maxOccurs
- Place in element reference
- These do not occur in element declarations
- These must be directect children of
- Local element declarations
- Appear entirely within complex type definitions
- Substituion-related attributes are not valid within local element declarations:
substitutionGroup
final
abstract
- Can declare as children elements of:
all
choice
sequence
- Attributes (only
name
is a required attribute) & attribute typeid
(ID
)name
(NCName
)form
- Use to set whether element-type name must be qualified in the instance
- Allowable values
"qualified"
"unqualified"
- Default value
- Value of
elementFormDefault
ofschema
- Note: that attribute in turn defaults to
"unqualified"
- Value of
type
(QName
) - to specify data typeminOccurs
(nonNegativeInteger
) - defaults to"1"
maxOccurs
(nonNegativeInteger
)- Can also use a value of
quot;unbounded"
- Defaults to
"1"
- Can also use a value of
default
(string
) - default valuefixed
(string
) - fixed valuenillible
(boolean
)- Controls whether
xsi:nil
can be used in the instance - Default =
"false"
- Controls whether
block
- allowable values:"#all"
, or:- List of:
"substituion", "extension"
, or"restriction"
- Defaults to
blockDefault
ofschema
- Allowable content
annotation?
(simpleType ¦ complexType)?
(key ¦ keyref ¦ unique)*
- Using global vs. local element declarations
- Use global element declarations when:
- "The element declaration could ever apply to the root element during validation." (Walmsley, p. 125)
- You want to use the element across complex types
- You want to use the element in a substituion group
- Use local element declarations when:
- "You want to allow unqualified element-type names in the instance." (Walmsley, p. 126)
- In this case the only global element you should have would be the root element
- Reminder: In an instance global element-type names are always qualified
- You want to (re-)use an element name in different locations, varying the element by data type or by some other property
- E.g., if making a schema that will include shirts & pants might want to create a
size
element that can be used (appropriately) in both - Would not create a global
size
element here because sizes for shirts might be S, M, L, XL, etc., while for pants would be 32, 34, 36, etc.
- E.g., if making a schema that will include shirts & pants might want to create a
- "You want to allow unqualified element-type names in the instance." (Walmsley, p. 126)
- Use global element declarations when:
- Global element declarations
- Declaring the data types of elements (3 options)
- Employ the
type
attribute in your element declaration- Must set value to a named data type
- Value can either be a user-derived type or a built-in type
- Define an anonymous type - i.e., specify either a
simpleType
orcomplexType
child - Do neither of the above - this 'defaults' to an
anyType
data type- Content can then be children and/or content
- Element can also then take any attributes
- Employ the
- Default & fixed values
- General
- Pertains to treatment of empty elements
- Schema will insert default or fixed values
- Differing treatment of elements vs. attributes with default or fixed values
- If an attribute with a default or fixed value is not even present the schema processor will effectively add the attribute (& value) to the appropriate element
- With elements nothing happens if the element itself is not present
- Again, default & fixed values only come into play with ( present, but) empty elements
- With elements the
default
&fixed
attributes are mutually exclusive - Elements in which
default
&fixed
attributes may be used:- Simple types
- Complex types with simple content
- Complex types with mixed content but only if all children are optional
- Default & fixed values &
minOccurs
&maxOccurs
settings- Specification of default & fixed values is independent of occurrence constraints
- If an element has
minOccurs
> 1 you can leave the element empty & allow element content to be populated by parser fromfixed
ordefault
values
- Default values
- As a reminder, default values come into play only where an element has empty content
- While empty elements with default values will have their content populated, certain data types allow an empty data string:
string
normalizedString
token
- Any user-defined types that derive from one of these types & which allows an empty string
- Unrestricted list types also allow empty values
- Issues with empty elements which allow an empty string
- If element has
xsi:nil
attribute set to"true"
the default value will not be inserted - Otherwise default value will be inserted
- If element has
- Whitespace
- Whitespace is typically not synonymous with an empty string
- For string data, then, whitespace sill not be replaced with default values
- For data types like
integer
, howeverwhitespace
facet of this data type iscollapse
- Whitespace handling occurs before default-processing handling
- Therefore, when an integer-type element with a default value is populated only with whitespace the element will be populated with the default whitepace Note :
xx
xx
xx
xx
- Again, this differs from a string-type element, where whitespace would be read as valid content
- Fixed values
- Somewhat suprisingly, you may want to supply content to an element even where the element has a fixed value
- Example: you have an element with an
integer
data type and a fixed value of1
- Acceptable content would be
1, 01, +1
, etc.
- Acceptable content would be
- General
- Nils & nillability
- General
- Using
xsi:nil
in an instance - Making elements nillable
- Qualified vs. unqualified forms
- Global & local element declarations
- Attribute Declarations
- General
- Global & local attribute declarations
- General
- Using attributes vs. elements
- Global attribute declarations
- Local attribute declarations
- Declaring attributes globally vs. locally
- Assigning types to attributes
- Default & fixed values
- General
- Default values
- Fixed values
- Qualified vs. unqualified forms
- Simple Types
- General
- Simple type varieties
- General
- Deciding on how much to break down data values
- Simple type definitions
- General
- Named simple types
- Anonymous simple types
- Using named vs. anonymous types
- Simple type restrictions
- General
- Defining a restriction
- Overview of the facets
- Inheriting & restricting facets
- Fixed facets
- When to fix a facet
- Facets
- General
- Bound facets
- Length facets
- General
- Allowing empty values
- Restricting the length of an integer
totalDigits & fractionDigits
- Enumeration
- Pattern
- Whitespace
- Preventing simple type derivation
- Regular Expressions
- General
- The structure of a regular expression
- Atoms
- General
- Normal characters
- Character class escapes
- General
- Single-character escapes
- Multi-character escapes
- Category escapes
- Block escapes
- Character class expressions
- General
- Specifying a list of characters
- Specifying a range
- Combining characters & ranges
- Negating a character class expression
- Parenthesized regular expressions
- Quantifiers
- Element Declarations
- Schema Components - Advanced
- Union & List Types
- Built-In Simple Types
- Complex Types
- Deriving Complex Types
- Advanced Schema Topics
- Reusable Groups
- Substitution Groups
- Identity Constraints
- Redefining Schema Components
- Schemas vs. DTDs
- Naming Considerations
- Extensibility & Reuse
- Bibliography
- Walmsley, Priscilla. Definitive XML Schema Upper Saddle River, NJ 07458: Prentice Hall PTR, 2002. Print.
- The Basics
- XLink 1.1
- General
- Benefits of XLink
- Can link more than 2 resources
- Can supply metadata to a link
- Can identify links that reside in locations other than the linked resources
- CSS & XLink
- Styling local elements from which XLink specifies traversal is possible
- W3 recommends such an element be treated as a hyperlink source anchor
- I.e., W3 recommends you format this the same as you would format an
a
element in HTML
- Benefits of XLink
- XLink Concepts
- Links & Resources
- Definitions
- Link - an explicit relationship between resources (or portions of)
- Linking element - an XML element that contains & "asserts" the existence of a link
- Participating in a link - those resources which are in a group to which a link associates
- The (6) XLink elements
- The (2) linking elements
simple
extended
- The (4) metadata elements
locator
- to address remote resources participating in an extended linkarc
- for information about how to traverse a pair of resourcesresource
- to supply local resourcestitle
- for human-readable labels
- These elements are the available values which can be used for the
xlink:type
attribute
- The (2) linking elements
- Resources
- While XLinks links must appear in XML documents, the pointed-to resources do not
- Hyperlinks are, of course, most common type of XLink links
- Resources can be for computer (vs. human, i.e. readable) consumption
- Definitions
- Arcs, Traversals, & Behavior
- Traversal
- Defined as using or following a link
- Though links can involve multiple resources, traversals always include but a pair of resources
- Starting resource = where traversal begins
- Ending resource = pointed-to resource
- Arc
- Defined as the information about how to traverse a pair of resources
- Direction of the traversal
- Can also contain information about application behavior
- A multidirectional link
- Defined: 2 arcs about the same pair of resources where the arcs differ only in which is the starting & which the ending resource
- This is not the same as "going back" after traversing a link
- Defined as the information about how to traverse a pair of resources
- Traversal
- Resources in Relation to the Physical Location of a Linking Element
- Local vs. remote resources - defined
- Both are XML elements
- Local resource is an XML element
- Participates in a link by being a linking element
- An XML element is also a local resource if a parent element is a linking element
- Remote resource
- Participates in a link by being addressed with an IRI reference
- This definition holds even if the remote resource is in the same XML doc as the link (or even in the same linking element!)
- Note: a third-party arc is one where the arcs go exclusively between remote resources
- Analogy - local resource is one defined "by value", remote is one defined "by reference"
- Linkbases
- The entities you employ where you want to originate a link from a source:
- Over which you have no write access
- Where you do not want to exercise write access
- Which offers no way to embed a linking construct
- In these cases you need to use an inbound or 3rd-party arc
- These arcs predictably more difficult to discover than outbound arcs
- Link databases, or linkbases, are documents containing collections of inbound & 3rd-party links
- Linkbases typically are built around some logical relationship among the contained links
- The entities you employ where you want to originate a link from a source:
- Local vs. remote resources - defined
- Links & Resources
- XLink Processing & Conformance [technical; ignored]
- XLink Markup Design
- General
- Namespace:
http://www.w3.org/1999/xlink
- Must prefix the namespace (generally with
xlink
) when declaring it - The namespace declares several global attributes (as global, these attributes must be prefixed):
- Functional
type
href
show
actuate
label
- Not synonymous with
id
attribute - Specifically, different XLink elements can share the same
label
value
- Not synonymous with
from
to
- Semantic
role
- indicates a property that the entire link hasarcrole
title
- human-readable description of an entire length
- Functional
- 'Defining' an XLink element
- XLink 1.0: this occurs with the presence of an
xlink:type
attribute - XLink 1.1: including either
xlink:type
orxlink:href
identifies an element as an XLink element
- XLink 1.0: this occurs with the presence of an
- Available values (XLink 1.1) for the
xlink:type
attribute"simple"
"extended"
"locator"
"arc"
"resource"
"title"
- Note: if an XML element contains an
xlink:href
attribute but noxlink:type
attribute then the element is treated as if it has anxlink:type
attribute with a value of"simple"
- All other attributes & elements defined in the namespace are reserved
- Namespace:
- XLink Attribute Usage Patterns
- Note: an XML element with, say, an attribute/value of
xlink:type="resource"
is referred to as aresource
-type element - Value chosen (or implied) for
type
attributes drives which other XLink attributes can or must be used - Unless flagged as mandatory these attributes are those which can be used within a given XLink-type element:
simple
-type element:href
role
arcrole
title
show
actuate
extended
-type elementrole
title
locator
-type elementhref
(required)role
title
label
arc
-type elementarcrole
title
show
actuate
from
to
resource
-type elementrole
title
label
resource
-type element: no other attributes (other thantype
) can be used
- Note: an XML element with, say, an attribute/value of
- XLink Element Type Relationships (i.e., which other XLink element types a given XLink element type can have as children)
simple
-type element (no XLink children allowed):extended
-type element can have the following XLink elementslocator
arc
resource
title
locator
-type element can have only atitle
-type XLink element as a childarc
-type element can have only atitle
-type XLink element as a childresource
-type element (no XLink children allowed):title
-type element (no XLink children allowed):
- Attribute Value Defaulting
- With XLink it's easy to wind up dealing with a large number of attributes
- Where appropriate you may supply default values in a DTD or a schema
- With DTDs be careful about putting these default values in an external subset - not all browsers will read that info!
- Not all schema languages allow you to specify default attribute values
- Integrating XLink with Other Markup
- Essentially there are no issues
- If so inclined you can use DTDs or Schema to tighten, say, which XLink elements can be children of which XLink parent elements
- Integrating XLink with Legacy Markup
- XLink requires the use of prefixed attributes
- Therefore, XHTML 1.0's
a
element is not a conforming XLink construct, despite having anhref
attribute
- General
- XLink Elements & Attributes
- General (Two Kinds of Links)
- Extended links
- Full XLink functionality
- Example: inbound links, third-party arcs, & arbitrary number of participating resources
- Can point to remote resources, specify arc traversal rules, etc.
- Structure can get fairly complex
- Full XLink functionality
- Simple links
- Used for an outbound link with 2 participating resources
- One resource must be local, the other remote
- As an outbound link, the arc must go from local to remote
- Same functionality as HTML
A
&IMG
elements
- Used for an outbound link with 2 participating resources
- Extended links
- Extended Links (
extended
-Type Element)- General
- Definition
- A link that associates an arbitrary number of resources
- Those resources can be any combination of local & remote
- Characteristics
- The only link that can have inbound & third-party arcs
- Note: an extended link can itself contain a local resource
- Can have fewer than 2 resources
- This obviously makes the link un-traversable, but it is not an error
- Might do this to associate properties with a single resource
- Might also create as a to-be-populated placeholder
- Deployment
- "Typically, extended linking elements are stored separately from the resources they associate (for example, in entirely different documents)." (W3 Recommendation, section 5.1)
- This deployment patterns makes extended links useful in following situations:
- Participating resources are read-only
- Easier/cheaper to modify a separate linking element than it is to modify participating resources themselves
- Resources have no native support for embedded links (e.g., media formats)
- Markup
- Can place neither
extended
- norsimple
-type element inside a parentextended
-type element - Sub-elements of
locator-, arc-
orresource-
type elements must be direct children of anextended
-type element - Allowable attributes (both semantic) of an
extended-
type element:role
title
- See Example in W3 standard, 5.1 Extended Links (
extended
-Type Element), for sample DTD & XML mark-up
- Can place neither
- Definition
- Local resources for an extended link (
resource
-type element)- The entire
resource
-type element is a/the local resource - Content
- Is allowed
- Any content, however, has no XLink-specified relationship to the link
- With an interactive XLink app where the link is a starting resource:
- That app will typically generate content
- Content will give the user a way to initiate the traversal
- Allowable attributes
role
(semantic)- Used to indicate the property of the resource
- The value is often a URI
title
(semantic)label
(traversal) - provides a way for anarc
-type element to refer to element
- The entire
- Remote resources for an extended link (
locator
-type element)- May have content, though any content will have no XLink significance to the link
- Any nested XLink children elements have no XLink relationship to the parent
- Attribute constraints
- Must contain the locator attribute (i.e., the
href
attribute) - The
href
attribute must have a value
- Must contain the locator attribute (i.e., the
- The
href
attribute identifies the remote resource - See Example in W3 standard, 5.1.2 Remote Resources for an Extended Link (
locator
-Type Element), for sample schema & XML mark-up
- Traversal rules for an extended link (
arc
-type element)- Content
- Allowable
- Only
title
-type element has any XLink significance
- Allowable attributes
- Traversal
from
to
- These 2 attributes take as their values the
label
values of appropriate resources - Further, these values specify how the link may start & end
- Omitting values for a
from
or ato
attributearc
attribute looks to all labels in thelocator
-type elements of the enclosingextended
-type link- The empty
to
and/orfrom
attributes then default to referring to that whole collection of labels - Omitting values for both attributes means that you get n-squared traversals, where n = number of
locator
-type elements - Omitting values for one or both attributes can/will mean that a resources wind up having themselves as both source & destination - this is not an error
- When multiple resources share the same label (permitted)
- These are understood to be individual resources, not a single resource in aggregate
- Using multiple-resource labels as values for
from
and/orto
attributes results in a Cartesian-product set of traversals
- Not identifying all possible traversal pairs
- Certainly allowable
- XLink does not impute anything here
- May see cases where a higher-level app does provide some default behavior
- Behavior
show
actuate
- Semantic
arcrole
- Corresponds to the RDF notation of a property
- "The semantic attributes describe the meaning of the arc's ending resource relative to its starting resource." (W3 standard, 5.1.3, "Traversal Rules for an Extended Link (
arc
-Type Element))- Example: and ending resource might be a person
- In relation to the starting resource, however, the ending resource might be "mother"
- One resource can serve as the starting point for multiple links
- This situation does not affect or interfere with traversal rules
- When using interactive applications might want to provide a pop-up for user to select from
title
- Traversal
- Constraint: no arc duplication
- Within any given
extended
-type element thearc
-type elements must be unique - Uniqueness is defined by the values of the
to
&from
attributes
- Within any given
- Content
- Titles for extended links, locators, & arcs (
title
-type element)- Can actually have multiple
title
-type elements - Might do this in situations where you need to provide a title in different languages (would use
xml:lang
attribute for that) - Can have content
- Can actually have multiple
- Locating Linkbases (special arc role)
- General
- XLink obviously needs to be able to find both starting & ending resources of a specified traversal
- Never a problem with an outbound link
- It is an issue, however, with inbound and 3rd-party links
- One typically uses linkbases to handle inbound & 3rd-party arcs
- Linkbases are designed to contain a collection of related linking elements
- Mechanics of defining a linkbase
- Set the linkbase as the value for the
to
attribute in yourarc
-type element - Include an
arcrole
attribute & set value to"http://www.w3.org/1999/xlink/properties/linkbase"
- Set the linkbase as the value for the
- For ease of processing, etc., linkbases are generally defined at the beginning of document
- Constraint: linkbases must be XML
- That is, a linkbase must be XML when it is the ending resource of an arc
- Loading linkbases
- In some ways similar to handling normal arc
- However, when traversing an arc with a linkbase as ending resource the entire linkbase is loaded for later use
- Also, "…XLink applications must suspend traversal of linkbase arcs at user option." ( XLink specification, section 5.1.5)
- How an XLink app is supposed to handle a linkbase arc:
- Keep track of starting resource
- After traversing a link to a linkbase app should extract any extended links from inside linkbase
- Assuming extracted portion is a valid XML file (or portion thereof) app should make available those extended links completely contained inside that extracted portion
- Timing of linkbase arc traversal - depends on value of
actuate
attribute"onLoad"
: linkbase is loaded & links extracted when starting resource is loaded- Note:
"show"
value must be ignored because presentation in this case is non-sensical
- Chaining linkbases
- Can be done
- Consuming app may chose to limit the number of steps it will process in a linkbase chain
- Misc. notes about consuming apps
- Should maintain a list of extended links retrieved after processing a linkbase
- Should not retrieve duplicate resources or links (this would arise in cases of cyclic dependencies)
- General
- General
- Simple Links (
simple
-Type Element)- Designed to provide shorthand for uncomplicated, outbound extended links
- Essentially rolls the following elements into one:
extended
-typelocator
-typearc
-type (direction implied)resource
-type
- Traversal arc is outbound, by implication
- Abilities you 'lose' with simple links:
- Supplying more than 1 remote & local resource (each)
- Creating in-bound arcs
- Specifying a title with the arc
- Specifying a role or title with the local resource
- Specifying a role or title with the link as a whole
- Content
- Allowable
- "The
simple
-type element itself, together with all of its content, is the local resource of the link, as if the element were aresource
-type element." (W3 standard, 5.2 Simple Links (simple
-Type Element)
- Absence of content
- Allowable
- Typically this is seen where it assumed consuming app will provide a way to give the user a way to traverse the link
- Note: use of the
href
attribute is not required- Obviously link is then un-traversable
- Might want to employ this construct if all you want to do is to associate properties with a resource
- XLink Element Type Attribute (
type
)- Again, this element determines the type of the link
- Constraint: type Value
- Except in case of
simple
-type element 'defined' by existence ofhref
attribute, a value for thetype
attribute must be supplied - Note: you can supply
"none"
as a value.- If you do so the element has no XLink significance.
- Might do this if you want an XLink app not to bother checking for the presence of an
href
attribute
- Except in case of
- Locator Attribute (
href
)- May be used on
simple
-type elements - Must be used with
locator
-type elements - Note: both XLink 1.0 and 1.1 are explicit about not making any syntactical demands of the value of the
href
attribute - Can use relative references
- Can use fragment identifiers
- May be used on
- Semantic Attributes (
role, arcrole,
&title
)- The values supplied for
role
orarcrole
attributes cannot be relative - Where the
title
attribute is used its value should be a descriptive, human readable string
- The values supplied for
- Behavior Attributes (
show
&actuate
)- General
- The two attributes are never required
- With
arc
-type elements in linkbase lists apps must assume:show="none"
actuate="onLoad"
- This applies even if different values were in fact specified
show
attribute- Used to specify how you want your ending resource to be presented
- Constraint:
show
values (required)"new"
- i.e., new window, frame, etc."replace"
- i.e., in existing window, frame, etc."embed"
- Assuming that the starting link is not the entire document, this is akin to loading a *.gif file in an HTML page
- The presentation of embedded resources is determined by the consuming app
"other"
- use this if your directions on how to present the resource are placed elsewhere in your markup"none"
actuate
attribute- Use to set the timing of arc traversal
- Constraint:
actuate
values (required)- These values give direction on how a consuming XLink app should behave
"onLoad"
- i.e., traverse the arc as soon as the starting resource is loaded"onRequest"
- Could set trigger to be user clicking on starting resource
- Could set trigger to be a timer countdown
"other"
- use when your directions are provided elsewhere in the markup"none"
- General
- Traversal Attributes (
label, from,
&to
)- None of these attributes is required
- Constraint:
label, from
&to
values- Must be a NCName
- Values for
from
&to
must be the value of alabel
attribute on alocator
- orresource
-type element
- General (Two Kinds of Links)
- Bibliography
-
"XML Linking Language (XLink) Version 1.1 Recommendation",
S. DeRose, E. Maler, D. Orchard, N. Walsh,
Editors. World Wide Web Consortium (W3C) Recommendation,
06 May 2010. This version of the XLink 1.1 Recommendation is available at
http://www.w3.org/TR/2010/REC-xlink11-20100506
. The latest version of the XLink Recommendation is always available athttp://www.w3.org/TR/xlink11/
.
-
"XML Linking Language (XLink) Version 1.1 Recommendation",
S. DeRose, E. Maler, D. Orchard, N. Walsh,
Editors. World Wide Web Consortium (W3C) Recommendation,
06 May 2010. This version of the XLink 1.1 Recommendation is available at
- General
- XSLT - 1.0 & 2.0 (incl. XPath 1.0 & 2.0)
- General (incl. XPath)
- General
- Design of XSLT
- General
- XSLT also referred to as a stylesheet, although it differs from CSS
- XSLT stylesheets are themselves XML documents
- This means that you can use XSLT stylesheets on other XSLT style sheets
- Note: if needed you can also apply XSLT stylesheets to XML schemas
- Syntax/Namespace:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet stuff -->
</xsl:stylesheet> - Styntax when using XSLT 2.0
- Leave namespace declaration as-is
- Simply change version number to 2.0
- XSLT based on pattern-matching
- In XSLT rules are called templates
- Variables
- XSLT was designed so that multiple stylesheets could be combined without interefering with each other
- One result of this design is that, unlike with other programming languages, variables are not, well, variable
- Once a variable is assigned a value that value cannot be changed
- Note: this treatment of variables borrows from functional programming languages (e.g., Lisp), where variables are immutable
- Looping, iteration & recursion
- Because you cannot change a variable in XSLT you cannot do typical
for
ordo-while
loops - Note: connot do a
for i = 1 to 10
loop because the value ofi
can only be set once - Iteration consists of doing certain set process on each item in a set that matches a pattern
- This effectively allows you to achieve what a
for
ordo-while
loop would - Recursion to be addressed below
- Because you cannot change a variable in XSLT you cannot do typical
- Design of XSLT 2.0
- XSLT 2.0 uses XPATH 2.0
- XSLT 2.0 (i.e., & XPATH 2.0) support XML schema
- Can now work with XML datatypes - e.g.,
xs:dateTime
- If working with a schema-aware processor can also workwith custom datatypes - e.g.,
xs:student
- Can now work with XML datatypes - e.g.,
- Integrates with XQuery
- General
- XML basics re: XSLT
- XLM document rules
- Tags versus elements (i.e., not synonymous)
- Namespaces
- Must use a prefix (standard is
xsl
) for XSLT namespace - Any markup not prefixed with
xsl
is assumed to be desired output
- Must use a prefix (standard is
- Namespaces [XSLT 2.0]
- XSLT 2.0 provides support for most data types in XML schema
- Also provides new datatypes for duration
- Programming interfaces for XML: DOM, SAX & others
- DOM
- W3 recommendation
- Provides a tree view of document (all emanating from the root node)
- Interfaces (language-neutral) provided by DOM
Node
- Basic interface of DOM
- All other interfaces extend this one
Document
Element
Attr
Text
- Note: the text of a DOM object is a child of that object, not a property of it
- This most often comes into play with text content of elements
- Text of an attribute is also treated as
Text
node
Comment
ProcessingInstruction
- DOM parsers
- Entire document is processed (i.e., into its tree structure) before control is released
- Ergo, there can be a long delay when handling large documents
- Although DOM parsers have similar, tree-based view of an XML doucment as does XSLT, the views are not identical
- SAX
- SAX is interactive
- Fires events as it finds things like the start of a document, the start of an element, etc.
- Therefore, with SAX you do not have to wait for the parser to process your entire document
- It's up to calling application to save items returned by SAX
- SAX is not the memory hog that DOM can be (i.e., with large documents)
- SAX does not give you the hierarchecal view of a document you get with DOM
- Note: per Tidwell (p. 15) most XSLT processors (as of 2008) are based on DOM view of XML doucments
- SAX is interactive
- Other programming interfaces
- DOM
- XSLT standards
- XSLT 1.0
- XSL transformations (XSLT) version 1.0
- XML path language (XPath) version 1.0
- XPath was originally part of the XSLT standard
- Because of usefulness & popularity, XPath subsequently became a separate standard
- XSLT 2.0 (& XPath 2.0)
- XSL transformations (XSLT) version 2.0
- XML path language (XPath) version 2.0
- XQuery 1.0 & XPath 2.0 Data Model (XDM)
- Defines how XPath 2.0, XSLT 2.0 & XQuery 1.0 organize data
- Also defines all legal values for expressions in XPath 2.0, XSLT 2.0 & XQuery 1.0
- XQuery 1.0 & XPath 2.0 functions & operators
- XQuery 1.0 & XPath 2.0 formal semantics (least useful spec for writing XSLT)
- XSLT 2.0 & XQuery 1.0 serialization
- XQuery 1.0: an XML query language (XQuery 1.0 is a superset of XPath 2.0)
- XML syntax for XQuery 1.0 (XQueryX)
- XSLT 1.0
- Miscellaneous XML standards
- The Extensible Stylesheet Language (XSL)
- Aka Formatting Objects specification or XSL-FO
- Deals with rendering XLM elements into formats such as audio, Braille, or PDF
- The Simple API for XML (SAX)
- Document Object Model (DOM)
- Associating stylesheets with XML documents
- Syntax/Example (for embedding the instructions within the XML document):
<?xml version="1.0"?>
<?xml-stylesheet href="docbook/html/docbook.xsl"
type="text/xsl"?>
<?xml-stylesheet href="docbook/wap/docbook.xsl"
type="text/xsl" media="wap"?> - Advantage of embedding in this fashion is that, as here, can specify different stylesheets to be used depending on type of browser or client type
- Disadvantage: generally want to avoid embedding processing instructions within data document
- Syntax/Example (for embedding the instructions within the XML document):
- XML pointer language (XPointer) version 1.0
- The Extensible Stylesheet Language (XSL)
- XLM document rules
- XSLT Processors
- Xalan
- Saxon
- Microsoft XSLT Processor
- Altova XSLT Engine
- How a stylesheet is processed
- Parsing the stylesheet - processor first reads the stylesheet
- Parsing the transformee - processor builds a tree view of xml document
- Processing steps
- Set any specified properties (usually a stylesheet specifies an output method)
- See if there are any nodes (in transformee document) to process
- This is always determined by the context - & context changes constantly
- At the start the context is always the root of the XML document
- Very first step is simply to identify the next node
- Once next node is found
- See if any
<xsl:template>
elements match that node - The element
<xsl:template match="/">
matches the node - i.e., the first or top node in an xml document - Note: The
<xsl:template match="/">
element is analagous to themain
method in C# - i.e., a required starting point for the program
- See if any
- If a
<xsl:template>
matches the next node…- Process the contents of the
<xsl:template>
element - Typically…
- The contents of a
<xsl:template>
element are at least one<xsl:apply-templates>
element(s) - The value of the
select
attribute inside the<xsl:apply-templates>
element correspond to thematch
attribute of another<xsl:template>
element
- The contents of a
- Note: if more than one
<xsl:template>
matches the next node the processor utilizes whichever is more specific - If no template matches the processor applies some built-in rules
- Process the contents of the
- Recursion
- Once processor finds the appropriate
<xsl:template>
element to match a document node that element may invoke another - Invoked
<xsl:template>
element can yet again invoke another, etc. - This is the recursion process
- Note: if more than one document element matches a <xsl:template>:
- The elements are processed in order
- This is referred to as document order
- Once processor finds the appropriate
- Generating HTML documents
- If (because!) you do not specify a namespace in your XSLT stylesheet for html elements, the processor passes through html tags unaltered
- However, the processor will at least think of them as xml tags
- Therefore, although html itself does not require this your style sheet must specify closing tags for elements like
<p>, <br>
, etc.
- Think of each
<xsl:template>
element as analagous to a rule in CSS
- Stylesheet structure
- The
<xsl:stylesheet>
element- Must include the
version
attribute (either"1.0"
or"2.0"
) - Must, of course, include the namespace declaration:
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- (Namespace declaration is the same for both versions of XSLT)
- Must include the
- The
<xsl:output>
elementmethod
attribute - values are:"xml"
"html"
"text"
"xhtml"
- XSLT 2.0 only
- Based on value chosen there are other attributes which can be used in conjunction
- The
<xsl:template>
element (withmatch
attribute) - Built-in template rules
- General
- Essentially the rules allow you to reach nodes in a document without having to wade through each parent node
- In other words, allows recursion to continue
- Specified templates always override built-in templates, though
- See Tidwell, p. 32, for the exact templates
- For element & document nodes
- For modes
- For text & attribute nodes
- For comment & processing instruction nodes - though, per Tidwell, this default template does nothing
- For namespace nodes - though, per Tidwell, this default template does nothing
- General
- Top-level elements
- General
- These are any elements which go at the top of an XSLT stylesheet
- Aka, any element which takes
<xsl:stylesheet>
as its parent
<xsl:include>
&<xsl:import>
- These are used to refer to other stylesheets
<xsl:import>
element- Anything in imported stylesheet has lower priority than calling stylesheet
- In object-oriented terms you are sub-classing the imported sheet
- Can only appear in beginning of sheet - i.e., can only take
<xsl:stylesheet>
as its parent
<xsl:include>
can be used anywhere in a stylesheet
<xsl:strip-space>
&<xsl:preserve-space>
- Both take
elements
attribute - Values of
elements
attribute are space-delimited list of elements to which the attribute applies - Can declare both elements so that you, say, strip space from all elements except those specified in
<xsl:preserve-space>
- Both take
<xsl:key>
(similar to declaring keys in a database)<xsl:variable>
- Used for variable declaration
- Any variable declared as a top-level element is global to the entire stylesheet
<xsl:param>
- Others
- General
- Other approaches
- There are typically a variety of ways to write a stylesheet while getting the same result/output
- If you have header or footer information you probably want that inside the
<xsl:template match="/">
element - Although you can often shorten/compact templates, it's often easier from standpoints of readability, debugging, & reuse to use multiple, simple templates
- The
- Design of XSLT
- XPath
- General
- Nodes & trees
- "…there is only one possible tree configuration for any given document…" (Ray, p. 100)
- Ergo, there is a unique path from document root to any point inside document
- XPath describes that route
- Trees & subtrees
- Think of document as collection of subtrees
- Note: a node tree must contain all appropriate opening & closing XML tags
- XPath & DOM
- XPath is designed to be used only in XSLT
- DOM is an API for any other progamming language (e.g., JS)
- "XPath is designed to be used inside an attribute in an XML document." (Tidwell, p. 45)
- XPath works with parsed version of an XML document
- Enitity references are resolved by processor before XSLT instructions are applied
- CDATA sections are converted to text
- No way of using XPath to get pre-parsed info on XML document
- XPath 2.0 vs. XPath 1.0
- No functionality from 1.0 is lost in 2.0
- XPath 2.0 built on concept of sequences rather than nodes
- Sequences can contain nodes
- Biggest difference between sequences & nodes is that sequences can contain atomic values
- Atomic values
- An
xs:integer
value of444
is an atomic value - Atomic values are defined in XML schema
- Atomic value is one which cannot be broken down into smaller parts
- An
- XPath 2.0 supports all XML schema built-in data types
- Note: schema-aware processors will also recognize custom schema-defined types
- Nodes & trees
- The XPath data model
- Node types
- Root
- Special kind of node - it is not an element!
- Contains:
- Document element
- Any comments &/or processing instructions surrounding document element
- Unlike all other nodes - it has no parent
- Always has at least document element as a child
- Using
<xsl:value-of select="/" />
gives you the text of all the root node's descendants, mashed together without spaces in between
- Element
- Root & elements are the only nodes which can contain other nodes
- An element can contain any other type of node except the root node
- An empty element is called a leaf node
- Getting the name of an element rather than its text content:
name()
- returns the element name plus any namespace in effect- Other functions exist to provide local name & the namespace
- Attribute
- Treating as a node allows you to access an element's attribute(s)
- Like an element node which contains only text
- Complications with attribute nodes:
- Parent/child relationship
- An element node is the parent of its attribute nodes
- However, an attribute node is not the child of its element node
- (An element's children are its text, other elements, comments, etc.)
- Attributes with DTD or schema-defined default values
- These attributes do not have to appear in the XML document
- What is more, XML parsers are not required to read an external DTD
- In that case the attributes will not even exist
- Usually must create a
<xsl:if>
or<xsl:choose>
hack to handle this
<xml:lang>
&<xml:space>
attributes are unique- The value of these attributes apply to all child elements
- However, XPath only sees these attributes within the element in which they are declared
- Parent/child relationship
- "…the only attribute nodes an element node contains are those tagged in the document and those defined with a default value in the DTD." (Tidwell, p. 49)
- Text
- When uninterrupted treated as a leaf node
- Always the child of an element node
- Note 1: an element can contain more than one text node; text can be broken up by elements or other node types
- Note 2: there are no such things as CDATA nodes; text inside a CDATA section is treated as a simple text node
- Comment - the node has a value but no name
- Processing instruction
- Includes the string value
- Can obtain its name (via
name()
function)
- Namespace
- A namespace declaration is not treated as simply another attribute node
- Because namespace declarations affect all child elements, a namespace is a region of a document, not of an element
- Namespace nodes are almost never used in XSLT stylesheets
- Notes on DTDs:
- DTDs are not treated as node types!
- XPath assumes all entity references are resolved prior to XPath looking into a document
- This approach is required since entities might resolve to XML mark-up
- Root
- Node Tests
- XPath 1.0 (4 node tests)
- The 4 node tests (these look like functions):
node()
- Matches any node
- Example:
attribute::node()
selects all attributes of the context node
text()
- any text nodecomment()
- any comment nodeprocessing-instruction()
- Obviously, returns any processing instruction
processing-instruction('for-web')
- any processing instruction with afor-web
target
- Other ways to retrieve/navigate nodes
*
- In the attribute axis any attribute (e.g.,
attribute::*
) - In the namespace axis any namespace
- In all other axes any element
- Example 1:
child::*
will match all the element children of a node - Example 2:
foo:*
will match all the elements in thefoo
namespace
- In the attribute axis any attribute (e.g.,
/
- Again, not synonymous with the root element
- Contains the root element plus all comments or processing instructions preceding root element
foo
- In the attribute axis the
foo
attribute of the context node - In the namespace axis the
foo
namespace - In all other cases the element
foo
- In the attribute axis the
- The 4 node tests (these look like functions):
- XPath 2.0 node tests
- Function-like tests
element()
- Takes up to 2 attributes
- Used without attributes it is the same as
select="*"
in XPath 1.0 - Used with one attribute it finds elements by name - e.g.,
element(foo)
returns all<foo>
elments - Use with 2 attributes to search by element name & data type - e.g.,
element(birthDate, xs:gYear)
- Can also use to find all elements of a specific data-type - e.g.,
element(*,xs:gYear)
schema-element()
- This must be used with an attribute
schema-element(foo)
will return all schema-defined<foo>
elements
attribute()
- Operates just like
element()
node test - Used without attributes it is the same as
select="@*"
in XPath 1.0 - Used with one attribute it finds attributes by name - e.g.,
attribute(author)
returns allauthor
attributes - Use with 2 attributes to find by attribute name & data type - e.g.,
attribute(birthDate, xs:gYear)
- Can also use to find all attributes that take values of a specific data-type - e.g.,
attribute(*,xs:gYear)
- Operates just like
document-node()
- Returns document nodes
document-node(element(foo))
gives you a document node with a single-element child of<foo>
- The document node will, of course, also include any comments and/or processing instructions
- Note: your processor will throw an error if the so-called document node has more than 1 child element
- Other node-retrieval approaches with XPath 2.0
*:NCName
- Gives your all nodes with the specified local name
- Example:
*:fName
will give you<student:fName>
,<author:fName>
, &<fName>
elements
- Note: re:
item()
- You see
item()
in conjunction with variable declarations - This is not a node test
- It is, instead, a datatype
- Example:
<xsl:variable name="foo" as="item()">
defines a variable named "foo" which can contain a single value (though that value can contain any node or atomic type)
- You see
- Function-like tests
- XPath 1.0 (4 node tests)
- Sequences & atomic values [XSLT/XPath 2.0]
- Sequences
- Conceptually much like arrays
- Can hold any kind of items - e.g., nodes or values
- Sequences have both an order & a length
- With XPath 2.0 functions can
- Extract subsets of a sequence
- Insert or delete items at a particular point
- Example (a variable containing a sequence):
<xsl:variable name="months" as="xs:string*"
select="('January', 'February', 'March',
'April', 'May', 'June',
'July', 'August', 'September',
'October', 'November', 'December')" /> - Syntax notes:
as
attribute defines datatype of variable- The asterisk in
"xs:string*"
signifies any number of values - Note: when no asterisk is present a variable can contain only a single value
- Atomic values
- Again, this is an XML concept
- When creating a variable with multiple atomic values can use
<xsl:sequence>
(with itsselect=
attribute) to obtain those values - Can then
- Use XPath expressions to obtain values
- 'Hard code' the desired values
- Employ a combination of both
- Syntax/example (assuming an xlm file with doc element of rockGroups):
<xsl:variable name="rockIdols" as="xs:string*">
<xsl:sequence select="rockGroups/brits/beatles"/>
<xsl:sequence select="('Townshend', 'Daltry', 'Entwistle', 'Moon' )"/>
<xsl:sequence select="('Page', 'Plant', 'Bonham', 'Jones' )"/>
</xsl:variable> - Note: the
$rockIdols
variable will be a sequence of 12 dead or aging British rock gods - The
as
attribute- In the
$rockIdols
example each item is of typexs:string
- Had we defined the variable with
as="item()*"
then:- The Fab Four items would have been element nodes
- Thw Who & Led Zeppelin entries would have been strings
- When stepping through the items in a sequence can use the
instance of()
operator to distinguish which items are which - See Tidwell, p. 54, for an example of this
- In the
- Sequences
- Node types
- Location paths
- The context
- General
- Context is a critical concept in XPath
- Context is akin to the current directory when working in DOS
- In DOS, results you get from a
dir *.*
command will depend entirely on the current directory - XPath expressions will similarly vary based on context (i.e., current location)
- The XPath 1.0 context
- Context equivalent to "the node in the tree from which any expression is evaluated." (Tidwell, p. 55)
- However, there are also environment variables to consider as part of context
- Five components to context
- Context node
- Synonymous with current directory
- All XPath expressions are evaluated from the context node
- Context position & context size
- These integer values are important when processing groups of nodes
- Context size is the number of items being processed by a given XPath expression
- Context size is the position of the particular item currently being processed within the group
- The variables (both names & values) currently in-scope
- All the functions currently available to XPath expressions
- Functions typically defined by either XPath or XSLT
- However, can also create functions within a stylesheet itself
- All of the namespace declarations currently in-scope
- Context node
- The XPath 2.0 context
- General
- Not surprisingly, there are more environment variables (i.e., context components) to consider with XPath 2.0
- XPath 2.0 specs discuss static context & dynamic context
- Static context defined
- The information available before an XPath expression is even processed
- Another way of saying this is that static context is the information which does not change while an expression is being evaluated
- Dynamic context defined
- Includes all static information!
- Also includes the information, variables which do change while an XPath expression is being processed
- Static context
- Whether the processor is in XPath 1.0 compatability mode (possible values are either
true
orfalse
) - Statically known namespaces (each item in set incluse both prefix & namespace URI)
- The default namespace for elements & types (either a namespace URI or
"none"
) - The default namespace for functions (either a namespace URI or
"none"
) - In-scope schema definitions (i.e., schema types, elements, or attributes)
- In-scope variables
- The static type of the context item
- In-scope function signatures - this includes:
- The namespace of each function
- The arity - i.e., number of parameters of a function
- Static types of both function parameters & of its result(s)
- Note: constructor functions are included in this group
- Statically known collations (this is implementation defined)
- Base uri
- The set of statically known documents (refers to documents available via the
doc()
function) - The set of statically known documents (relates to the
collection()
function) - The statically known default collection type
- Unless the XSLT processor has overridden things the default collection type is
node()*
- Driven by
collection()
function
- Unless the XSLT processor has overridden things the default collection type is
- Whether the processor is in XPath 1.0 compatability mode (possible values are either
- Dynamic context
- General
- General
- Simple location paths
- Relative & absolute expressions
- Selecting things besides elements with location paths
- Attributes
- The text of an element
- Comments, processing instructions, & namespace nodes
- Using wildcards
- Axes
- General
- Unabbreviated syntax
- Axis roll call
- Predicates
- General
- Numbers in predicates
- Functions in predicates
- The context
- Attribute value templates
- Datatypes
- Datatypes in XPath 1.0
- Datatypes in XPath 2.0
- XPath Operators
- General
- Mathematical Operators
- Addition (
+
) - Subtraction (
-
) - Multiplication (
*
) - Division (
div
) - Integer division (
idiv
) - XPATH 2.0 - Modulo (
mod
) - Unary minus (
-x
) - Unary plus (
+x
)
- Addition (
- Boolean operators
- Comparing expressions
- Comparing atomic values [XPATH 2.0]
- Comparing sequences [XPATH 2.0]
- Conditional expressions -
if, then,
&else
[XPATH 2.0] - Iterators over sequences - the
for
operator [XPATH 2.0] - Quantified expressions -
some
&every
[XPATH 2.0]- General
- Complications
- Range expressions - the
to
operator [XPATH 2.0] - Constructor functions [XPATH 2.0]
- Datatype operators -
instance of, castable as, cast as,
&treat as
[XPATH 2.0] - Set operators -
except, intersect,
&union
[XPATH 2.0] - Node operators [XPATH 2.0]
- The
is
operator - node-after (
>>
) - node-before (
<<
)
- The
- Comments in XPath expressions - [2.0]
- Types of XSLT 2.0 Processors
- The XPath view of an XML document
- Output view
- The stylesheet
- General
- General
- Creating Output
- General
- Generating Text
- General
- Creating simple text
- Outputting the value of something
- Changes to
<xsl:value-of>
in XSLT 2.0
- Numbering Things
- General
- Changes to
<xsl:number>
in XSLT 2.0
- Formatting Decimal Numbers
- [2.0] Formatting Dates & Times
- Using
<xsl:copy>
&<xsl:copy-of>
- Whitespace
- General
- Whitespace basics
- Using
<xsl:preserver-space>
&<xsl:strip-space>
- The
normalize-space()
function - A simple technique for adding whitespace to tet output
- Branching & Control Elements
- General
- Branching Elements of XSLT
- Invoking Templates by Name
- Parameters
- Variables
- Recursion
- Emulating a For Loop with a Style Sheet
- Creating Links & Cross-References
- General
- Using the XML
ID, IDREF
, &IDREFS
Datatypes - XSLT's Key Facility
- Generating Links in Unstructured Documents
- Sorting & Grouping Elements
- General
- Sorting Data with
<xsl:sort>
- [2.0] The
<xsl:perform-sort>
Element - Grouping Nodes
- [2.0] New Grouping Syntax in XSLT 2.0
- Combining Documents
- General
- The
document()
Function - The
document()
Function & Sorting - Implementing Lookup Tables
- Grouping Across Multiple Documents
- [2.0] Features
- Using XSLT 2.0 to simplify things
- The
doc()
&doc-available()
functions - The
collection()
function - The
unparsed-text()
&unparsed-text-available()
functions
- Extending XSLT
- Bibliography
- Ray, Erik T.. Learning XML. 2nd Ed. United States of America: O'Reilly, 2003. Print.
- Tidwell, Doug. XSLT. 2nd Ed. United States of America: O'Reilly, 2008. Print.
- General (incl. XPath)
- XML 1.0 (incomplete)
- The Semantic Web
- XHTML & the Semantic Web (i.e., RDFa)
- Overview - Symantic Web & RDF
- Triples (aka Statements) & Graphs
- Components of a Triple
- Subject
- Predicate (i.e., Property)
- Object (i.e., value of the Property)
Graph
- A collection of triples
- Subjects and objects (but not predicates) of triples in a graph can be nodes
- The subject &/or predicate in a triple can also be a
blank node
(aka, abnode
) - A
graph
comprises nodes linked by their relationships
- Components of a Triple
- URIs & Triples
- URIs always used as subjects and predicates
- URIs can be used as objects
- Literals
- Used only for objects
- Plain Literals
- Simple text
- Note: Plain literals are placed inside quotation marks to distinguish plain literals from relative URIs
- Typed Literals
- A string, followed by a URI to indicated precisely why type of information is being given
- Syntax
- The text/string is enclosed within quotation marks
- The string is followed by two carats - i.e., ^^
- The "typing" is followed by the URI
- That URI appears within angled brackets, without interior quotation marks
- Most commonly-used "typing" URI: XMLSCHEMA, at
http://www.w3.org/2001/XMLSchema
- CURIEs (& Turtles)
- RDF Language
- RDF can only work with syntax that resolves to complete URIs
- Note: RDFa is a subset of RDF
- Ergo, any & all relative URIs in RDFa or RDF must first be resolved
- CURIE: Compact URI
- CURIEs are a syntax for condensing the repetitive portions of long URIs
- Specifically, a CURIE replaces the repeated portions of long URIs with a substitute
token
- Defining/declaring
tokens
- Option 1:
- Utilize XML namespace syntax
- Example (using DBpedia.org):
<div>xmlns:db="http://dbpedia.org/"</div>
- Option 2:
- Utilize
Turtle
syntax - Example (using DBpedia.org):
@prefix dbp: <http://dbpedia.org/property/>
- Note: In Turtle syntax
<>
represents a URI referring to the current document
- Utilize
- Within a to-be-shortened URI the
token
typically replaces:naming scheme
machine name
- The repeated portion of the
path
element - Note: see my HTML Outline section on URIs for definitions
- Option 1:
token
scopetoken
definitions (i.e., namespace declarations) of course occur within some element within the XHTML markuptoken
scope: valid within the element in which it is defined and within all descendent elements
- CURIE Syntax
- A CURIE is a URI broken into 2 parts (those 2 parts are separated by a colon):
prefix
- (the portion of the URI which will be replaced with a token)reference
- (the non-repetitive portion of URIs)
- Syntax variation 1
- Can sometimes omit the
prefix/token
, leaving just colon &reference
- When this occurs in RDFa the 'default prefix' mapping is
http://www.w3.org/1999/xhtml/vocab#
- Can sometimes omit the
- Syntax variation 2
- Use an underscore (i.e., '_') for the
prefix/token
- This refers to a
bnode
, or blank node
- Use an underscore (i.e., '_') for the
- Syntax variation 3
- Omit both the
prefix/token
& colon, leaving just thereference
- RDFa, however, does not support this syntax
- Omit both the
- A CURIE is a URI broken into 2 parts (those 2 parts are separated by a colon):
safe CURIEs
- Simply equivalent to placing a
CURIE
within square brackets safe CURIEs
are used whenever a parser might have difficulty distinguishing between a CURIE and a URI- Example:
<div xmlns:db="http://dbpedia.org/">
<div about="[db:resource/Albert_Einstein]">
…
</div>
</div>
- Simply equivalent to placing a
- CURIEs & Relative Paths
- Best Practice: avoid using relative paths when declaring CURIEs
- However, once an RDFa processor concatenates an "expanded" CURIE with the resource, processor will attempt to resolve any relative URIs by using the
base
declaration
- RDF Language
- Triples (aka Statements) & Graphs
- RDFa Overview
- RDF vs. RDFa
- RDF apparently requires that attributes and values be contained within element tags
- RDFa is designed to re-use existing markup as much as possible
- Add markup to specify properties
- However, can allow existing element content to be used as property values
- This reduces mark-up - it also reduces chances for error
- RDFa & Vocabularies
- Unlike Microformat, vocabularies are not defined in the markup
- References to external vocabularies enhances standardization
- Common Vocabularies
- bibo:
http://purl.org/ontology/bibo/
- cc:
http://creativecommons.org/ns#
- dbp:
http://dbpedia.org/property/
- dbr:
http://dbpedia.org/resource/
- dc:
http://purl.org/dc/elements/1.1/
- dcterms:
http://purl.org/dc/terms/
- foaf:
http://xmlns.com/foaf/0.1/
- rdf:
http://www.w3.org/1999/02/22-rdf-syntax-ns#
- rdfs:
http://www.w3.org/2000/01/rdf-schema#
- taxo:
http://purl.org/rss/1.0/modules/taxonomy/
- xhv:
http://www.w3.org/1999/xhtml/vocab#
- xsd:
http://www.w3.org/2001/XMLSchema#
- bibo:
- RDFa DTD & Related Specifications
- XML Declaration - recommended
- Doctype Declaration:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
"http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"> - Namespace Declaration(s):
- Often placed within
html
tag - Example (using RDFa & 2 other namespace declarations):
<html xmlns="http://www.w3.org/1999/xhtml"
version="XHTML+RDFa 1.0" xml:lang="en"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
- Often placed within
- Media-Type Declaration
- Mark-up should include the following
meta
element:
<meta href="content-type" content="application/xhtml+xml">
- Character sets
- If a specified in the (top-of-file) xml declaration,
charset
definition should be repeated in the media-type declaration - Example (using the UTF-8 character set):
<meta href="content-type" content="application/xhtml+xml; charset=UTF-8">
- If a specified in the (top-of-file) xml declaration,
- Mark-up should include the following
- RDF vs. RDFa
- RDFa Attributes
- Existing (i.e., HTML 4.0/XHTML 1.0) Attributes (& their values)
rel
- Used to define predicates in RDF statements (i.e.,
triples
) - value: whitespace-separated list of CURIEs
- Used to define predicates in RDF statements (i.e.,
rev
- Used to define predicates in RDF statements (i.e.,
triples
) - value: whitespace-separated list of CURIEs
- Used to define predicates in RDF statements (i.e.,
content
- Used to define objects in RDF statements (i.e.,
triples
) - value: string, or 'plain literal object' (in RDF terminology). See RDFa Literals
- Note:
content
can be used to "over-ride" or to "restate" an element's actual contents - Example–the author within a bibliographical citation of War and Peace
<span property="dc:creator" content="Leo Tolstoy">Tolstoy, Leo</span>
- Used to define objects in RDF statements (i.e.,
href
- Used to define an object in RDF statements (i.e.,
triples
) - value: URI
- Used to define an object in RDF statements (i.e.,
src
- Used to define an object in RDF statements (i.e.,
triples
) - value: URI
- Used to define an object in RDF statements (i.e.,
- New, RDFa-Specific Attriubutes
about
- what the data are about- Used to define the subject in an RDF statement (i.e., a
triple
) - value: a URI or a Safe CURIE
- Used to define the subject in an RDF statement (i.e., a
property
- relationship between a subject and some literal text- Used to define predicates in RDF statements (i.e.,
triples
) - value: whitespace-separated list of CURIEs
- Used to define predicates in RDF statements (i.e.,
resource
- the (typically) 'non-clickable' partner resources of a relationship- Used to define objects in RDF statements (i.e.,
triples
) - value: whitespace-separated list of CURIEs
- Used to define objects in RDF statements (i.e.,
datatype
- Used to specify datatype of literal objects in RDF statements (i.e.,
triples
) - See Also: RDFa Typed Literals
- value: a CURIE
- Used to specify datatype of literal objects in RDF statements (i.e.,
typeof
- Used to specify RDF types (i.e., vocabularies) to associate with a subject
- value: whitespace-separated list of CURIEs
- Example: (when referring to a book in mark-up…)
- Assuming the following namespace declarations in parent/ancestor element(s)…
xmlns:biblio="http://example.org/"
xmlns:dc="http://purl.org/dc/elements/1.1/" - Would write:
<apan about="urn:ISBN:1234567890" typeof="biblio:book" property="dc:title">Some Book Title</apan>
- Assuming the following namespace declarations in parent/ancestor element(s)…
- Attributes & Triples
Subjects
- usually referenced using theabout
attributePredicates
- usually referenced via the following attributes:property
rel
rev
Objects
- usually referenced via the following attributes:- When using a URI to define/set the object:
href
resource
source
- When using a text to define/set the object:
content
- Or, simply, the content of the containing element in question
- When using a URI to define/set the object:
- Existing (i.e., HTML 4.0/XHTML 1.0) Attributes (& their values)
- Bibliography
-
"RDFa Primer",
B. Adida, M. Birbeck,
Editors. World Wide Web Consortium (W3C),
14 October 2008. This version of the Working Group Note is available at
http://www.w3.org/TR/2008/NOTE-xhtml-rdfa-primer-20081014/
. The latest version of this Primer is always available athttp://www.w3.org/TR/xhtml-rdfa-primer/
. -
"RDFa in XHTML: Syntax and Processing.",
B. Adida, M. Birbeck, S. McCarron, S. Pemberton,
Editors. World Wide Web Consortium (W3C),
14 October 2008. This version of the RDFa in XHTML: Syntax and Processing Recommendation is available at
http://www.w3.org/TR/2008/REC-rdfa-syntax-20081014/
. The latest version of this Recommendation is always available athttp://www.w3.org/TR/rdfa-syntax
.
-
"RDFa Primer",
B. Adida, M. Birbeck,
Editors. World Wide Web Consortium (W3C),
14 October 2008. This version of the Working Group Note is available at
- Overview - Symantic Web & RDF
- Vocabularies, Namespaces & Ontologies of Interest (incomplete)
- Dublin Core Metadata Initiative (DCMI)
- Dublin Core Metadata Element Set [DCMES]
- Legacy versus current Element Set
- Current Element Set
- Implemented January 2008
dcterms:
namespace -http://purl.org/dc/terms/
- Elements now can include formal domains and ranges
- Domains & ranges specify what kind of described resources are associated with a given property
- Obviously can be more exact than natural-language descriptions
- Element names are unchanged - ergo compatible with pre-January 2008 Element Set
- Technically, the 15 elements have been defined as (new) subproperties of the corresponding properties of DCMES Version 1.1
- Legacy Element Set
dc:
namespace -http://purl.org/dc/elements/1.1/
- Domains and ranges have not been specified for the fifteen properties of the dc: namespace
- Legacy Element Set still valid & usable
- Use of the
dcterms:
namespace is encouraged, however
- Current Element Set
- The Elements (15)
- Contributor - can be a person, organization, or service
- Coverage (1 of 3 types)
- Spatial (i.e., location)
- Topic or applicability
- Can be a named placed or geographic coordinates
- Best Practice: use a Thesaurus of Geographic Names [TGN]
- TGN:
http://www.getty.edu/research/tools/vocabulary/tgn/index.html
- Temporal (i.e., time period)
- Jurisdiction
- Spatial (i.e., location)
- Creator - can be a person, organization, or service
- Date
- Can be point of time or a time period
- Best Practice: use an encoding scheme, such as the W3CDTF profile of ISO 8601:
http://www.w3.org/TR/NOTE-datetime>
- Description - can be an abstract, a table of contents, a graphical representation, or free-text
- Format
- The file format, physical medium, or dimensions (size or duration) of the resource.
- Best Practice: use a controlled vocabulary, such as the list of Internet Media Types [MIME]
- Identifier
- Should be unambiguous within context
- Best Practice: Identify the resource by means of a string conforming to a formal identification system.
- Language
- Publisher
- Relation - a related resource
- Rights - typically a statement about various property (including intellectual) rights associated with the resource
- Source - a related resource from which the described resource is derived (in whole or in part)
- Subject - the topic of the resource
- Typically use keywords, key phrases, or classification codes
- Best Practice: use a controlled vocabulary
- Title
- Type
- The nature or genre of the resource
- Best Practice: Use a controlled vocabulary such as the DCMI Type Vocabulary [DCMITYPE]
- Note: URI for each term is either:
http://purl.org/dc/terms/ELEMENT_NAME
fordcterms:
(i.e., current) namespacehttp://purl.org/dc/elements/1.1/ELEMENT_NAME
fordc:
(i.e., legacy) namespace
- Legacy versus current Element Set
- DCMI Metadata Terms [DCMI-TERMS]
- The 'master' (i.e., larget) set of vocabularies and technical specifications
- Includes:
- Sets of resource classes, including the DCMI Type Vocabulary [DCMI-TYPE]
- Vocabulary & syntax encoding schemes
- Terms in DCMI vocabularies are intended to be used in combination with terms from other, compatible vocabularies in the context of application profiles
- Use of DCMI vocabularies (and others) to be done on the basis of the DCMI Abstract Model [DCAM].
- Explanatory documentation
- Includes:
- The 'master' (i.e., larget) set of vocabularies and technical specifications
- Dublin Core Metadata Element Set [DCMES]
- The Bibliographic Ontology (bibo)
- General
- Namespace:
http://purl.org/ontology/bibo/
- Designed for bibliographical data
- Incorporates/works with the following ontologies:
- foaf
- dcterms
- event
- prism
- Explanatory documentation
- Namespace:
bibo
Classes (69)- 'Proprietary' (i.e., prefix using
bibo
) - 58 total- AcademicArticle
- Article - use for magazine, newspaper, etc. articles
- Bill - refers to draft legislation
- Book
- BookSection
- Chapter
- CollectedDocument
- Collection
- Document
- DocumentPart
- EditedBook
- Excerpt
- Interview
- Issue
- Journal
- Magazine
- Newspaper
- Periodical
- Quote
- ReferenceSource
- Report
- Series - usually refers to books
- Standard - a document describing a standard
- Webpage
- Website
- Workshop
- 32 others - a number, but not all, have to do with legislation, court filing, etc.
- Incorporated classes
- foaf - 5 classes
- rdf - 3 classes
- 3 other, misc., non-bibo classes
- 'Proprietary' (i.e., prefix using
bibo
Objectproperties (52)- 'Proprietary' (i.e., prefix using
bibo
) - 30 total- authorList
- contributorList
- editor
- editorList
- 26 others
- Incorporated Objectproperties
- dcterms - 12 total
- contributor
- format
- publisher
- rights
- subject
- title
- 6 others
- event - 6 total
- foaf - 3 total
- rdf:value
- Used to describe the content of a bibo document
- Used in lieu of deprecated bibo:content Dataproperty
- dcterms - 12 total
- 'Proprietary' (i.e., prefix using
bibo
Dataproperties (54)- 'Proprietary' (i.e., prefix using
bibo
) - 37 total- chapter
- edition
- isbn
- isbn10
- isbn13
- numPages - total number of pages within a document
- shortTitle
- volume
- 29 others
- Incorporated Dataproperties
- dcterms - 4 total
- created
- date
- description
- issue
- foaf - 3 total (each relates to "name")
- prism - 9 total
- 1 miscellaneous (localityName)
- dcterms - 4 total
- 'Proprietary' (i.e., prefix using
- General
- Friend-of-a-Friend (foaf)
- General
- Explanatory documentation
- Namespace:
http://purl.org/ontology/bibo/
- Class names are in title case - e.g.,
OnlineChatAccount
- Property names are in camel case - e.g.,
workInfoHomepage
- Classes (13 Total)
- Agent
- Group
- Image
- OnlineAccount
- Organization
- Person
- PersonalProfileDocument
- Project
- 5 Others
- Properties (61 total)
- FOAF Categories
- Basics
- Classes
- Agent (stable)
- In FOAF generally - though not always - want to use
Person
- Subclasses:
Group, Person, Organization
- In FOAF generally - though not always - want to use
- Person (stable)
- 18 properties
- Used with
knows
- subClassOf:
Agent, Person, Spatial Thing
- Can be dead or alive, real or fictional
- Agent (stable)
- Properties
- Name-related properties (none is stable)
- Note: Cultural differences make name-related properties problematic
name
(testing)nick
(testing)- Designed for screen names, user names, & the like
- More specific properties (e.g.,
yahooChatID
) are available
surname
(testing)familyName
(testing)givenName
(testing)firstName
(testing)lastName
(unstable)familyName
&givenName
generally preferred where a last name "must" be identified- Might be appropriate with legacy data
title
(unstable)- Initially intended for Dr., Mrs., etc.
- Likely to be replaced with
honorificPrefix
- homepage (stable)
- mbox (stable)
- An
mbox
can have at most one owner at any one time - Typically identified using the
mailto:
URI scheme
- An
- mbox_sha1sum (testing)
- Represents an attempt to protect personal mailbox identities
- Applies something called the "SHA1 mathematical functional" to a
mailto:
URI
- im & depiction (both: testing)
- Name-related properties (none is stable)
- Classes
- Personal Info (properties only)
weblog
knows
interest
currentProject
pastProject
plan
based_near
age
workplaceHomepage
workInfoHomepage
schoolHomepage
topic_interest
publications
geekcode
myersBriggs
dnaChecksum
- Online Accounts / IM
- Classes
OnlineAccount
OnlineChatAccount
OnlineEcommerceAccount
OnlineGamingAccount
- Properties
account
accountServiceHomepage
accountName
icqChatID
msnChatID
aimChatID
jabberID
yahooChatID
skypeID
- Classes
- Projects & Groups
- Classes
Project
Organization
Group
- Properties
member
membershipClass
- Classes
- Documents & Images
- Classes
Document
Image
PersonalProfileDocument
- Properties
topic (page)
primaryTopic (primaryTopicOf)
tipjar
sha1
made (maker)
thumbnail
logo
- Classes
- Basics
- Related & Incorporated Namespaces, Vocabularies & Ontologies
- General
- XML Schema Datatypes (xs)
- General
- Namespace:
http://www.w3.org/2001/XMLSchema
- Documentation:
http://www.w3.org/TR/xmlschema-1/
- Namespace:
- General
- Bibliography
-
"FOAF Vocabulary Specification 0.97" 3rd Ed.,
D. Brickley, L. Miller,
1 January 2010.
This version (rdf, wiki) of the Namespace Document is available at
http://xmlns.com/foaf/spec/20100101.html
. The latest version is always available athttp://xmlns.com/foaf/spec/
.
-
"FOAF Vocabulary Specification 0.97" 3rd Ed.,
D. Brickley, L. Miller,
1 January 2010.
This version (rdf, wiki) of the Namespace Document is available at
- Dublin Core Metadata Initiative (DCMI)
- XHTML & the Semantic Web (i.e., RDFa)
- Database Management
- Access 2010
- Overview
- Databases
- Relational databases
- A table = a relation = information about a single subject
- Columns = attributes
- Rows = instances
- Relationships
- One-to-many
- Many-to-many
- Database capabilities
- Data definition
- Data manipulation
- Data control
- Access as an RDBMS
- General
- Desktop database:
*.accdb
or*.mdb
(pre-AXS '07) files - Access project (i.e., Access front-end):
*.adp
files- Typically MSSS is the back-end
- Can also use Microsoft SQL Server Desktop Engine (MSDE) as the RDBMS
- Desktop database:
- Data definition & storage
- Validation rules
- Data type
- Data ranges
- More complex validation rules (e.g., referential integrity)
- Formatting
- ActiveX controls (e.g., calendar control)
- Attachment data type
- New in AXS 2007
- Used for storing images & other data types
- Complex data
- This allows storage of multiple attachment files in a single record
- Significantly reduces bloat experienced with Attachments (versus pre-'07 AXS versions)
- Validation rules
- Data manipulation
- Building AXS queries
- SQL
- Data control
- Single data source
- Multiple-user environments
- Integration with SharePoint for security
- Offline, secured lists
- Workflow support
- Ability to undo changes via Recycle Bin
- Data encryption via algorithms
- Locking mechanisms (AXS recognizes & can work with locking mechanisms of other RDMSs)
- General
- Relational databases
- Access 2010 Interface
- Basics/Notes
- Backstage view (i.e.,
tab)
- When using AXS will close the open db in order to make a copy
- Tabs
-
- Link on upper-right to view database properties
- Buttons
-
- Can set number of recent dbs to show on Backstage view
- Can clear checkbox at bottom to not show any recent dbs in Backstage view
- Can pin dbs to top of menu
- Can R-click a db name for more options (including removing a db from your list)
-
(default tab)
- Shows templates
- Contains a search box for other templates
- contains several web templates
- Screen has Back, Forward & Home navigation buttons
-
- operates on the object which has focus
- Note: this may not be the object on the screen
-
-
options
- - creates a Cabinet file (*.CAB)
- - execute-only version of db
- - publish db to a document manager server
- - saves a web app to a client object format
- - publishes to a MS SharePoint site to become an Access Services app
-
options
-
- button - used for a lot of app-wide settings
- Quick Access Toolbar (customize via drop-down arrow at right edge)
- Can pick from 9 other commands with a single click
- Can drill down via
- Customization can be AXS-wide or for a single db
- Can also add macros to Quick Access Toolbar
- Make sure macro applies to current db!
- After adding a macro to QAT can click
button to:
- Assign icon to macro
- Define text
- Formatting
- Can rearrange order of items in QAT with & buttons
- Note: can add a Separator bar to QAT
- Shortcut
- Can right click on any command and select Add to Quick Access Toolbar
- Added command will be in place AXS-wide
-
- Backstage view (i.e.,
tab)
- Content security
- General
- Trust Center interface (& trusted locations)
- Any database with even queries is assumed to be unsafe by AXS
- Enabling a database that is not trusted
- Select
button in Security Warning message bar
-
option
- Database is added to Trusted Documents (ergo, you will not receive any security warnings again)
- Note: if you move the database it will no longer be recognized as a Trusted Document (ergo, you will be prompted anew about security)
-
- Use this if you want to enable content only for your existing session
- Note: AXS will close the database and then re-open it with macros & queries enabled
- If you select odds are the database will not work, as even queries will be disabled
-
option
- Note: when working with a still-untrusted db, the db is identified as such on the Info tab of the Backstage view
- Select
button in Security Warning message bar
- Trust Center options
-
- For digitally signed applications
- MSFT apps trusted by default
- - for designating folders & subfolders
-
- Check-box for allowing documents on a network drive to be Trusted (default: enabled)
- Check-box for disabling Trusted Documents
- Button to all Trusted Documents (i.e., removes documents from the internal list)
- (3 check boxes)
-
- 4 radio buttons, including:
-
button
- When a VBA project exists disables all ActiveX & displays the Message Bar
- Without VBA enables SFI, disables UFI ActiveX controls, & displays Message Bar
- If you in turn enable UFI ActiveX controls the will be initialized but with restrictions
-
button
- Default for new installations of AXS
- Behavior again depends on presence/absence of a VBA project
- See Conrad & Viescas Table 2-1, pp. 53-4 for details
-
button
-
check-box
- Selected by default
- Enables SFI ActiveX controls, but in safe mode
- 4 radio buttons, including:
- (4 radio buttons)
-
(4 radio buttons)
- DEP = Data Execution Prevention
- Prevents poorly written code from running on your computer
- Selected by default
- Look at category of Trust Center to see if DEP is preventing any add-ins from running
-
- Two radio buttons to enable/disable radio buttons
- A check-box to enable message logging
- (6 check boxes)
-
- General
- Office Fluent Ribbon
- General
- Contextual tabs appear to right of tab
- After clicking on any tab you can scroll through the others by using the scroll wheel on your mouse
- Commands within a tab are divided into various labeled
- Can add an entire group to QAT by right-clicking on the group's name, then working through the short-cut menu
- Tabs
- (Groups & commands on the tab will vary depending upon whether or not you are using pre-'07 formats)
- Collapsing/minimizing the ribbon - methods
- Double-click on any tab
- Ctrl+F1
- Click on the small Minimize The Ribbon button/icon next to the Help button (upper R of screen)
- R-click inside Ribbon and use short-cut menu
- Customizing the Ribbon
- Accessing the controls
- R-click in Ribbon & select
- Working with the customization dialog box
- The drop-down selection (under Customize the Ribbon) refers to contextual tabs
- Unchecking an item does not remove it from the ribbon - unchecking simply tells AXS not to display that item
- When creating a new Group associating an icon with the group comes into play only if you add that group to the QAT
- In Tabs pane (R-hand pane)
- R-click on groups gives you an option not to display labels next to commands
- R-click on commands in the Tabs pane to rename, move up, etc.
- Default Ribbon
- Can rename & reorder tabs
- Can rename & reorder groups
- Can neither rename nor reorder individual commands
- Importing/exporting ribbon customizations
- Can be done
- When you export customization you are also exporting any QAT customizations
- Accessing the controls
- General
- Navigation Pane
- General
- Once you click on an item in a group you can type a letter to jump to the next item which begins with that letter
- F11 toggles the Navigation Pane open/shuttered
- Navigation Pane object views
- Excellent way to check on table dependencies
- Select under Navigate to Category
- Select either or select a particular table under Filter by Group
- Critical to look at this before making any changes to table design
-
& selections
- Objects are then ranked in descending order
- Filter by Group will populate with and others
- = older than
- Excellent way to check on table dependencies
- Custom categories & groups
- General
- AXS creates a Custom Category whenever you created a new db
- With a blank db the Custom Category will contain only one (empty) group, Unassigned Objects
- Navigation Options dialog box
- To open R-click on Navigation title bar & select
- Will not be allowed to delete built-in categories
- Clearing the check box next to a group name means that the group will not be displayed in the Navigation pane
- Opening objects with a double-click is the default selection
- Creating & modifying a custom category
- Can either
- Click button, or
- If the unused Custom category still exists rename that
- AXS populates new, custom categories with 2 Groups
- Custom Group 1 (empty)
- Unassigned Objects
- Contains all object not assigned to other groups
- This group can be neither renamed nor deleted
- Can either
- Creating & modifying groups in a custom category
- When clicking on a category or group Move Up & Move Down buttons appear to R
- Ordering restrictions on categories & groups
- Categories: Custom categories must always go below &
- Groups: Unassigned Objects always goes at the bottom of the its category's groups
- Creating object shortcuts in custom groups
- Follow this step to populate your custom groups
- In Unassinged Objects
- Select objects by using Shift or Ctrl keys
- Right-click on one of the objects, select , then select group
- Can also drag-and-drop
- If you want a shortcut in more than one group
- Create the shortcut in the first group
- Then Ctrl-drag that shortcut into the other group(s)
- Objects in groups
- What you are creating in custom groups are only shortcuts to the objects
- Shortcuts are indicated by small arrow in lower-left corner of icon (a la Windows shortcuts)
- Ergo, deleting an "object" in a custom group only deletes the shortcut - you are not touching the object itself
- Hiding custom groups in a category
- R-click on title of Navigation Panel to get back into Navigation Options dialog box
- Clear the check-box next to the group you want hidden from view
- Hiding & renaming object shortcuts
- Note: subforms are a good example of an object which you might want to hide from the user
- 2 how-to options - right click on shortcut you want to hide & select:
- , or
- (then click check box)
- Note: you can also disable Design View of an object via the View Properties dialog
- As with Windows, renaming a shortcut does not rename the underlying object
- Revealing hidden shortcuts
- Use Navigation Options dialog box
- Click check box
- Note, however, that objects viewed this way will appear dimmed in the Navigation Pane
- Right click on shortcut to unhide it
- Reset the check box
- General
- Sorting & selecting views
- Manually sorting objects
- Searching for objects
- General
- Single-document vs. multiple-document interface
- Global settings & the AXS Options dialog box
- Basics/Notes
- Access 2010 Architecture
- General
- Object types
- Tables
- Queries
- Forms
- Reports
- Macros
- Modules
- Project (*.ADP) files
- Databases
- Creating a Database & Tables
- Designing Tables
- Creating a new database
- General
- Using a database template
- Creating a new empty dataset
- Table creation methods
- General
- Entering data
- Using Application Parts
- Using Data Type Parts
- Creating in Design View
- Defining fields
- General
- Field data types
- Setting field properties
- Completing field definitions inside a table
- Defining simple field validation rules
- Defining input masks
- Table-level settings
- Defining a primary key
- Defining a table validation rule
- Other table properties
- Cross-table relationships
- Simple
- Relationships on multiple fields
- Indexes
- Single-field
- Multiple-field
- Miscellaneous
- Setting table design options
- Creating a default template for new databases
- Printing a table definition
- Database limitations
- Creating a new database
- Modifying a Table Design
- General
- Backing up tables
- Checking object dependencies
- Table-wide modifications
- General
- Deleting tables
- Renaming tables
- Modifying fields
- Renaming fields
- Moving fields
- Inserting fields
- Copying fields
- Deleting fields
- Modifying data attributes
- General
- Changing data types
- Changing data lengths
- Handling conversion errors
- Changing other field properties
- Other tools & considerations
- Reversing changes
- Using the Table Analyzer Wizard
- Lookup properties
- Multi-value lookup fields
- Changing the primary key
- Compacting a database
- General
- Designing Web Tables
- Working with the Web
- Creating web databases
- General
- Using a database template
- Creating a new empty web database
- Creating a web database by entering data
- Creating a web table using Application Parts
- Using Data Type parts
- Creating a web table in Datasheet View
- Defining web fields
- Web field data types
- Setting field properties for web databases
- Completing fields in a web table
- Creating calculated fields
- Defining field validation rules for web databases
- General
- Handling tables
- Defining a table validation rule for web databases
- Defining a primary key for web databases
- Other web table properties
- Handling fields
- Creating lookup fields in a web database
- Creating relationships using lookup fields
- Defining a Restrict Delete relationship
- Defining a Cascade Delete relationship
- Using the web compatibility checker
- Analyzing the web compatibility issues table
- Preparing a client database for the web
- Creating Table Data Macros
- Importing & Linking Data
- Designing Tables
- Building Queries
- Simple Queries
- Complex Queries
- Modifying Data with Action Queries
- Forms & Reports
- Using Forms
- Building Forms
- Customizing Forms
- Advanced Form Design
- Using Reports
- Constructing Reports
- Advanced Report Design
- Automating an Access Application
- Event Processing
- Macros
- Automating Web Applications Using Macros
- Using VBA with Access
- Linking Access & the Web
- Web Applications in a Browser
- Using Business Connectivity Services
- Finalizing an Application
- Finishing Touches
- Distributing an Application
- Designing a Database Application
- Exporting Data
- Bibliography
- Viescas, John L. and Jeff Conrad. Microsoft® Office Access™ 2007: Inside Out. United States of America: Microsoft Press, 2007. Print.
- Overview
- RDMSs & SQL - General
- Database Types
- Flat Files
- Hierarchical
- Tree structure
- Parent/child model (each child has one & only one parent)
- Table is not a phrase used in hierarchical databases
- Advantages
- Can be extremely fast processing certain types of queries
- Handles one-to-many relationships well
- Limitations
- Must have intimate knowledge of database structure to write queries
- Must begin with a pointer to the root before drilling down to find an object
- Structure itself is very inflexible
- Can often be quite redundant
- Cannot enter parent-less children
- Cannot easily handle many-to-many relationships, or multiple-parent relationships
- Queries are very complicated to write - generally requires programming
- Network Databases
- Similar to hierarchical databases, but with multiple parents allowed
- Relationships among objects are called sets
- Advantage vs. hierarchical database is that you do not have to begin at a root in order to navigate to a data element
- Limitations
- Very inflexible
- Changes to the database structure often require rebuilding the entire database
- As with hierarchical database, queries are very complicated to write
- Object-Oriented Databases (these have receded in popularity)
- Online Analytic Processing (OLAP)
- Transforms data into a multidimensional object
- Used for advanced analysis
- Generally an add-on to RDBMS
- XML
- Relational Databases, Set Theory & Predicate Logic
- Set Theory
- A set is any collection (M) of definite & distinct objects (m), which themselves are called elements
- The set itself is a single entity
- Note: If the elements are not distinct then you are dealing with a multiset, or a bag
- Tables in a database are supposed to represent a set
- Rows in a table must therefore be distinct (enforced via key constraints)
- The definition of a set is rather subjective, though, again, it should be some real-world or logical entity
- Defining elements
- Done so strictly via their attributes
- Order of elements in the set is completely irrelevant
- Predicate Logic
- A predicate is a property or expression that either holds or not - i.e., it is either true or false
- Predicates used in relational model to:
- Maintain the logical integrity of the data
- Define its structure
- Sometimes you define sets simply by enumerating the members - e.g. Tom, Dick, & Harry
- Often, though, define a set by defining a property - e.g., all prime numbers
- Example: in a table of employees might enforce a predicate "salary greater than zero"
- Also use predicates when formulating queries - e.g., salary greater than $50,000
- The Relational Model
- General
- Combination of set theory and predicate logic
- Mathematical underpinning of RDMSs
- Keys
- Candidate key
- "…a set of one or more columns that has a unique value for every row in this table." (Kriegel and Trukhnov, p. 17)
- Essentially by definition, a candidate key contains non-null values
- Note: A table can have any number of candidate keys
- Primary key
- A particular case of a candidate key; the candidate key chosen to identify each record uniquely
- A table can have only one primary key
- Technically you are not required to have a primary key, though it is always a good idea to do so
- Composite key - a key composed of more than one column
- Foreign key - a field which contains entries from the primary key of a different table
- Candidate key
- Propositions, predicates, & relations
- Relations
- Relation = a representation of a set in set theory
- In the relational model relation is "a set of related information, with the implementation of the database being a table." (Ben-Gan, p. 5)
- A single relation/table should represent a single set
- Note: a join between two or more tables produces a single relation
- Attributes
- The heading of a relation comprises a set of attributes
- Note: Because those attributes are themselves a set their order does not matter
- Two components of an attribute
- Attribute name
- Domain - i.e., type, or range of possible values
- Example: for phone numbers would set domain to 10-digit entries that do not begin with 0 or 1 (in reality there are likely other constraints)
- Note: can also use custom enumerations, such as, for example, Full-time, Part-time, or Temp
- "A domain or type is one of the simplest forms of a predicate in our database because it restricts the attribute values that are allowed." (Ben‑Gan, p. 6)
- Propositions
- "A proposition is an assertion or statement that must be true or false." (Ben-Gan, p. 5)
- To define a relation you create predicates out of propositions - i.e.,
- This defines the structure of a relation
- Basically, you decide on/define the attributes needed to define a relation
- Relations
- Missing values
- Two-valued predicate logic: a proposition is either true or false
- Three-valued predicate logic: true, false, or unknown
- In SQL you flag unknown values with
NULL
- Constraints
- Domain integrity
- Selecting the type of an attribute
- Also specifying whether it accepts
NULL
values
- Many other constraints can be enforced
- Candidate keys to enforce entity integrity
- Foreign keys to enforce referential integrity
- Minimum, maximums, other options
- Domain integrity
- Normalization
- "Normalization is a formal mathematical process to guarantee that each entity will be represented by a single relation." (Ben-Gan, p. 7)
- 1st normal form (1NF)
- Two components:
- Rows in the table must be unique
- Attributes should be atomic
- Note: this is essentially a tautology (i.e., it defines a set/relation)
- Implementation
- Uniqueness of rows achieved by defining a unique key in the table
- "Atomicity of attributes is subjective in the same way that the definition of a set is subjective." (Ben-Gan, p. 8)
- When storing names, for instance, subjective decision as to whether you need to store first & last names as separate fields
- Sub-atomic attributes: having an address attribute that doesn't call for including city & state
- Arrays
- These do not ipso facto violate the 1st normal form
- Example: a Yearly Sales relation with the attributes qty2009, qty2010, & qty2011 is simply a relation of 3 years of data
- Two components:
- 2nd normal form (2NF)
- Rule 1: Data must meet 1NF
- Rule 2: "For every candidate key, every nonkey attribute has to be fully functionally dependent on the entire candidate key." (Ben-Gan, p. 8)
- In other words, need each candidate key value to obtain a nonkey attribute value
- Conversely, if you can obtain a nonkey attribute value given just part of a candidate key then you have violated the 2NF
- Example (of violating 2NF):
- An Orders relation
- Primary key: orderid, productid
- Noncandidate keys: orderdate, qty, customerid, companyname
- This violates 2NF because you know orderdate, customerid & companyname simply from orderid
- Must break into 2 tables: Orders & OrderDetails
- An Orders relation
- 3rd normal form (3NF)
- Rule 1: Data must meet 2NF
- Rule 2: "Also, all nonkey attributes must be dependent on candidate keys nontransitively." (Ben-Gan, p. 9)
- This means all nonkey attributes must be mutually independent
- Above example would violate 3NF because customerid & companyname are related
- Must create a 3rd table: Customers
- 2NF & 3NF summarized with "Every nonkey attribute is dependent on the key, the whole key, and nothing but the key - so help me Codd." (Ben-Gan, p.9)
- General
- Set Theory
- SQL - General Concerns
- SQL queries are always stored as simple ASCII files
- Syntax for comments:
- Use a double dash (
--
) for single lines - Use
/* */
to "comment out" multiple lines
- Use a double dash (
- Statement categories
- Data Definition Language (DDL)
- Deals with object defintions
- Typical statements:
CREATE
ALTER
DROP
- Data Manipulation Language (DML)
- Used for querying & modifying data
- Typical statements:
SELECT
INSERT
UPDATE
DELETE
MERGE
- Data Control Language (DCL)
- Deals with permissions
- Typical statements:
GRANT
REVOKE
- Data Definition Language (DDL)
- Syntax vs. Logical Execution of Single-Table Queries
- Syntax
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
- Logical execution
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
- Syntax
- Bibliography
- Gen-Gan, Itzik. Microsoft SQL Server 2008: T-SQL Fundamentals United States of America: Microsoft Press, 2009. Print.
- Kriegel, Kriegel and Boris M. Trukhnov. SQL: Second Edition. Indianapolis, IN: Wiley Publishing, Inc., 2008. Print.
- Database Types
- T-SQL (2016) (incomplete)
- SQL Basic Concepts
- General
- SQL is explicitly non-procedural
- Historically, no looping or branching statements in ANSI SQL
- SQL employs set-based procedures
- There is effectively no flow to SQL
- SQL statements either execute as a block or not at all
- Syntax conventions (BNF) & special characters
- UPPERCASE - SQL keywords
- <chevron_enclosed> - user-supplied parameter(s)
- ¦ (pipe)
- Separates syntax items enclosed in brackets or braces
- You can use only one of the items
-
[ ]
- optional items -
[,…n]
- brackets- Indicates the preceding item can be repeated n number of times
- The occurrences are separated by commas
-
[…n]
- brackets- Indicates the preceding item can be repeated n number of times
- The occurrences are separated by blanks
-
{ }
- Required items
- Sometimes also used to group items so that they can be marked with symbols such as [ ], ¦, etc.
-
*
- Identifies items that can be repeated 0 or more times -
( ) . ,
- punctuation that must be used
- SQL Data Types
- General
- Must specify data type for each column in a table
- SQL:2003 supports three categories of data types:
- Predefined
- Constructed
- User-defined
- Predefined data types
- Strings
- Character strings
- General
- Character sets
- A predefined sequence of characters
- "A 'character set' could be thought of as a table that assigns a unique binary number … to each character that belongs to the character set." (Kriegel & Trukhnov: 2nd Ed., p. 47)
- ASCII is the simplest, & most common - 1 byte/character
- Unicode = 2 bytes/character (& sometimes 4)
- Collation
- The set of rules that govern comparisons between characters in a set
- Typically defines 4 items
- Language support - e.g., Latin1_General
- Dictionary sorting
- Normal: A & a < B & b
- Binary: A < B < a < b
- If a collation uses binary sorting it will contain
BIN
in its name
- Case sensitivity
CI
in name indicates "case insensitive"- If case insensitive then sorting regards a = A
- Accent sensitivity
AS
in name indicates "accent insensitive"- If accent sensitive then à <> ä
- Levels of specification
- For on-premise SQL Server
- Instance - i.e., the entire server
- Database
- Column
- Expression
- For Azure SQL
- Database
- Column
- Expression
- For on-premise SQL Server
- Setting database collation
- Use
COLLATE
clause to override the instance collation - Sets the default column collation
- Sets collation for metatdata objects
- If case insensitive then you cannot have two tables
T1 & t1`
- Note: collation for variables & parameters controlled by instance collation, not by database collation
- If case insensitive then you cannot have two tables
- Use
- Column & expression collation
- Use
COLLATE
clause to define column collation - Instance collation
- Case-insensitive example:
-- The following will return a record for 'John Doe'
SELECT lastname
FROM HR.Employee
WHERE lastname = 'doe'; - Overriding case-insensitive db collation example:
-- The following will not return a record for 'John Doe'
SELECT lastname
FROM HR.Employee
WHERE lastname COLLATE Latin1_General_CS_AS = 'doe';
- Case-insensitive example:
- Use
- Strings of zero length
- Called an empty string
- SLQ:2003 specifically differentiates between empty strings &
NULL
- However, this is not the case with all RDBMS vendors!
- Size
- ASCII characters occupy 1 byte (8 bits)
- Characters from other character sets might be larger
- Character sets
- Fixed-length character strings
- The system will allocate the requisite number of bytes in memory
- Blank characters are padded to the end of any entries that are shorter than the specified length of the column
- (Ergo, all data is made to be the fixed-length)
- Use when you expect values to be roughly the same size
- Syntax (2 variations):
CHARACTER(n)
, wheren
specifies the length of the stringCHAR(n)
- If size is omitted the default is 1
- Character strings of varying length
- Must specify maximum length
- System allocates memory/disk space dynamically
- Use when you expect varying sizes
- Syntax (3 variations):
CHARACTER VARYING(n)
, wheren
specifies maximum lengthCHAR VARYING(n)
VARCHAR(n)
- National character strings
- ASCII (1 byte/character) allows for 256 different characters
- Unicode
- Standard, double-byte (i.e., 4-byte) character set
- Allows over 4 billion characters
- Encompasses every character across all languages
- When working with Unicode there are two types, which mimic ASCII data (i.e., fixed length and varying length)
- Note: With Unicode, if you specify a fixed length of 13 characters the field will occupy 52 bytes (13 X 4)
- Fixed-Length Syntax (4 variations):
NATIONAL CHARACTER(n)
NATIONAL CHAR(n)
NCHAR(n)
CHARACTER[(n)] CHARACTER SET <char_set_name>
- Variable-Length Syntax (4 variations):
NATIONAL CHARACTER VARYING(n)
NATIONAL CHAR VARYING(n)
NCHAR VARYING(n)
CHARACTER VARYING(n) CHARACTER SET <char_set_name>
- Specify that a string literal should be treated as National Character
- Pre-pend the literal with the (capital) letter 'N' to specify that it should be treated as National Character
- Note:
WHERE lastname = N'Doe'
- Large objects
- Sometimes need to store data that will exceed a vendor's character string type limit
- Examples: Resumes, term papers, etc.
- ASCII-data syntax (2 variations):
CHARACTER LARGE OBJECT
CLOB
- Unicode syntax:
NATIONAL CHARACTER LARGE OBJECT
NCLOB
- Character string literals
- Enclosed in single quotation marks - i.e., apostrophes
- Use 2 apostrophes to represent a single apostrophe within a string literal
- To represent a national character set literal precede the string literal with a capital N
- Example: N'Bill White'
- General
- Binary strings
- General
- Used to store binary information
- Examples: a document in Word format, audio & video files, program executables
- Syntax (2 variations):
BINARY LARGE OBJECT
BLOB
- Binary string literals
- Can technically define them (SQL Server & Oracle)
- Rarely used in practice
- Binaries typically accessed and manipulated via special programs & interfaces
- General
- Character strings
- Numbers
- Exact numbers
- General
- 2 Variations
- Whole numbers
- Decimals
- Characteristics
- Positive/negative
- Precision - i.e., maximum total number of digits than can be stored (both before & after decimal)
- Scale - i.e., maximum number of decimals
- 2 Variations
- Types & syntax
NUMERIC[(p[,s])]
- Use for whole numbers and numbers with specific decimal component
- Can optionally specify scale
DECIMAL[(p[,s])]
, orDEC[(p[,s])]
- Similar to
NUMERIC
- However, the precision (but not the scale) used by a vendor-specific implementation can be greater than the precision used in the declaration
- Similar to
INTEGER
, orINT
- 4-byte data
- Range: -2.1B to +2.1B
SMALLINT
- 2-byte data
- Range: -32.7K to +32.7K
BIGINT
- 8-byte data
- Range: -9.2 quintillion to +9.2 quintillion
- Literals for exact numbers
- Precede with a plus (optional) or minus sign
- Include a decimal point (optional)
- General
- Approximate numbers
- General
- The number pi, for instance, cannot be specified to exact precision
FLOAT[(p)]
- Stores floating point numbers
- Specifying precision is optional
REAL
- Similar to
FLOAT
- However, precision is fixed
- Similar to
DOUBLE PRECISION
- Very similar to
REAL
- However, greater precision
- Very similar to
- Literals for approximate numbers
- Basically same as literals for exact numbers
- However, use uppercase
E
or lowercasee
- Examples:
+4.56E2
-3.345e1
7.876E-2
- General
- Exact numbers
- Date & time
- Complex data types
- Any 'single' date/time entry actually contains several bits of information: year, month, etc.
- In programming these kinds of data types are called complex & are usually created with the
Structure
keyword
- Date & time implementations
- Syntax:
- Note (on UTC, or Coordinated Universal Time):
- Equivalent to Greenwich mean time (GMT)
- Time zones are either positive or negative offsets to UTC
DATE
(3 elements):- Year (4 digits)
- Month & day (2 digits each)
TIME
(3 elements):- Hour (2 digits, from 00 to 24)
- Minute (2 digits)
- Seconds (2 digits, 00 to 61 - to handle leap seconds)
TIME WITH TIME ZONE
- adds UTC plus time zone informationTIMESTAMP[(p)]
- A combination of
DATE
&TIME
- Can optionally specify precision
- A combination of
TIMESTAMP[(p)] WITH TIME ZONE
INTERVAL
(2 options):- Include
YEAR
&MONTH
fields - Include
DAY
,HOUR
,MINUTE
, &SECOND
elements
- Include
- Note (on UTC, or Coordinated Universal Time):
- Date & time literals (implementation varies significantly by vendor)
- Syntax:
- Complex data types
- XML data-type implementations
- XML data is hierarchical
- Implementation varies by vendor
- Strings
- Constructed & user-defined data types (implementation varies by vendor)
- Other data types
BOOLEAN
- Noteworthy because several major vendors do not implement
- See Kriegel & Trukhnov: 2nd Ed., p. 77, for how to simulate
- Vendors have introduced a variety of other, proprietary data types, e.g.:
ROWID
,UROWID
(Oracle)TIMESTAMP
(SQL Server 2008)SQL_VARIANT
(SQL Server 2008)
NULL
- SQL:2003 standards state that each data type should contain a
NULL
value - Any operator that includes
NULL
as a term must returnNULL
- This often requires special work-arounds
- Example: if you have a calculation with 100 -
NULL
you probably don't wantNULL
as your result
- SQL:2003 standards state that each data type should contain a
- General
- General
- Creating & Modifying Database Objects (i.e., DDL)
- Creating RDBMS Objects
- Tables
- Syntax (simplified):
CREATE [ # ¦ ## ] TABLE <table_name>
(
<column_name> [<domain_name> ¦
<data_type> [<size1>[, <size2>]]]
[
GENERATED [ALWAYS ¦ BY DEFAULT]
AS IDENTITY <identity_clause>
]
[<column_constraint>,…]
[DEFAULT <default_value>]
[COLLATE <collation_name>],…
[<table_constraints>]
) - Simple example:
USE DB_NAME;
DROP TABLE IF EXISTS dbo.Employee;
CREATE TABLE dbo.Employee
(
-- EF looks for a primary key named 'ID'
ID INT PRIMARY KEY
,firstname VARCHAR(30) NOT NULL
,lastname VARCHAR(30) NOT NULL
,hiredate DATE NOT NULL
,mgrid INT NULL
,ssn VARCHAR(20) NOT NULL
,salary MONEY NOT NULL
,CONSTRAINT U_firstname_lastname
UNIQUE(firstname, lastname)
,CONSTRAINT fk_mgrid FOREIGN KEY (mgrid)
REFERENCES HR.Manager (ID)
ON DELETE CASCADE
ON UPDATE CASCADE
) - Temporary tables
- Only the data in a temporary table is transitory; the table definition itself is permanent
- Data exists only until user
- Commits changes in another table
- Logs off
- Typically used:
- When issuing SQL commands from other programs (i.e., embedded SQL)
- You need intermediate results to perform complex calculations
- Two types (types differ by data visibility)
GLOBAL
- data can be accessed by any program or module within the sessionLOCAL
- Columns
- Column definitions (in SQL:2003 can use domains rather than data types when defining columns)
- Column constraints
- Columns can have multiple constraints
- Options:
NOT NULL
(Ummm…NULL
values are not permitted)UNIQUE
(Note:NULL
values are permitted)PRIMARY KEY
(a combination ofNOT NULL
&UNIQUE
)REFERENCES
(the column is a foreign key to the referenced table)CHECK
- Verifies that column values obey certain rules
- Example: only positive numbers, or only certain/enumerated values
- Naming constraints
- Optional, though RDBMS will generate default name if you do not specify one
- Syntax (within the
CREATE TABLE
command, & ignoring datatype sizes):
<colname1> <datatype> CONSTRAINT <constraint_name1> <constraint1> [<constraint2>, etc.],
<colname2> <datatype> CONSTRAINT <constraint_name2> <constraint1> [<constraint2>, etc.],
… - Conventions used when naming constraints (prefixes or suffixes, sometimes with 01, 02, etc. appended)
NOT NULL
- nnUNIQUE
- uk, uPRIMARY
- pkREFERENCES
- fkCHECK
- chk, c, or words like "UPPER", "POSITIVE", etc.
- Column default values (specified via
DEFAULT
keyword withinCREATE TABLE
command) - Column collating sequence (allows you to specify non-standard string ordering rules)
- Table constraints
- General
- Very similar to column constraints
- Difference is that table constraints can operate on multiple columns
- (Single-column constraints can also be declared as table constraints)
- Options:
UNIQUE
(similar to the column constraint but can be used to ensure the uniqueness of a combination of two or more columns)PRIMARY
- If specifying multiple columns the combination of values must be unique
NULL
values are not allowed
FOREIGN KEY
(if multiple columns are identified each column must reference a primary key in another table)CHECK
- Syntax:
- Similar to syntax for column constraints
- However: following the keyword specifying the type of constraint (e.g.,
PRIMARY
) the column name(s) and/or rules must be enclosed in parentheses. - Example (adapted from Kriegel & Trukhnov: 2nd Ed., p. 667):
CREATE TABLE Address(
addrid INTEGER NOT NULL,
address VARCHAR(60),
type VARCHAR(8),
city VARCHAR(18) NOT NULL,
state CHAR(2),
zip VARCHAR(10) NOT NULL,
CONSTRAINT CHK_Address_type
CHECK (type IN ('BILLING', 'SHIPPING')),
CONSTRAINT PK_Address PRIMARY KEY (addrid)
);
- Referential integrity constraints
- Can optionally specify how the row in a child table is affected when a referenced row in a parent table is changed or deleted
- Syntax:
[
ON {DELETE ¦ UPDATE}
{NO ACTION ¦ CASCADE ¦ SET NULL ¦
SET DEFAULT ¦ RESTRICT}
] - Options:
NO ACTION
- Default value
- Means the user will receive an error message if attempting to delete a row or to update the primary key value of a referenced row
CASCADE
- If a referenced row is deleted all child rows will be deleted!
- If a referenced row's primary key is updated the foreign keys of child rows will be similarly updated
SET NULL
- child row's foreign key values will be set toNULL
whenever the primary key of a referenced row is deleted or changed- Child row's foreign key values will be set to
NULL
whenever the primary key of a referenced row is deleted or changed - Note: you will get an error message when running your
CREATE TABLE
command if the column with the foreign key is defined asNOT NULL
- Child row's foreign key values will be set to
SET DEFAULT
- Foreign key column(s) in a child row are set to their default values whenever a referenced row is deleted or the primary key of a referenced row is changed
- Obviously, this assumes that the foreign key columns in a table with child rows have default values
RESTRICT
- Similar to
NO ACTION
, in that an integrity constraint violation error is raised - Difference:
NO ACTION
checks are made at the end of an SQL statement - Therefore,
NO ACTION
allows referential constraints to be "temporarily" violated RESTRICT
, however, prohibits any such violations, even when transitory
- Similar to
- Deferrable constraints
- Constraints can either be
DEFERRABLE
orNOT DEFERRABLE
(default) NOT DEFERRABLE
constraints are checked after every DML statementDEFERRABLE
constraints have 2 options:- Immediate mode: checked after every
INSERT, DELETE
, orUPDATE
statement - Deferred mode: Constraints are checked at the end of the transaction
- Immediate mode: checked after every
- Deferrable constraints are helpful when:
- You might want to load data into a child table before loading data into a parent table
- You want to load data which doesn't comply with
CHECK
constraints and then go in and "fix" that data
- Constraints can either be
- General
ON COMMIT
clause- Used only with temporary tables
- Specifies whether data are deleted at the end of a transaction or kept through the end of a session
- Physical properties clause
- Deals with how files are physically stored on disks
- General idea:
- Separate database objects by type
- Spread objects across available disks to speed up database operations
- As a rule, you would put table data on one disk, table indexes on other, etc.
- Implementation varies by vendor
- Identity clause
- Used to generate unique, sequential values
- Generally used for a primary key
- Syntax:
[
[START WITH <integer>]
[INCREMENT BY <integer>]
[MINVALUE <integer>]
[MAXVALUE <integer>]
[CYCLE ¦ NO CYCLE]
]
- Creating a new table as a copy of another table
- Can create from 1 table or from multiple tables
- Can create new column names or keep existing ones
- Syntax:
CREATE TABLE <table_name>
[(
<col1_name>,
<col1_name>,
…
)]
AS
(
SELECT …
FROM …
WHERE …
)
[WITH NO DATA]
- Syntax (simplified):
- Indexes
- General
- Physical files; exist on a hard drive
- Indexes speed data retrieval
- Generally one does not explicitly refer to indexes within query statements
- Essentially 2-column tables
- Column 1: values from a column or from a group of columns in referenced table
- Column 2: pointer to physical row location on disk
- Note: these tables are sorted on "Column 1" values
- Clustered indexes
- Phsically stores data rows in a table based on key values
- There can be only 1 clustered index per table because key values must be unique
- Implementation
- Typically implemented as B-tree indexes
- B-tree algorithm
- Designed to minimize number of hard disk reads
- Uses a binary tree (i.e., each node has only 2 children)
- If the top value in the node tree is greater than the value being sought you go to the "left-hand" child of the node
- Otherwise you go to the "right-hand" child of the node
- You continue until you find the desired value (or you reach a leaf node)
- See Kriegel & Trukhnov: 2nd Ed., Fig. 4‑1, p. 116, for a good, explanatory diagram
- Indexes can either be unique or non-unique
- Indexes are implicitly unique when created on columns with a
PRIMARY KEY
orUNIQUE
constraint - In fact, indexes on
PRIMARY KEY
orUNIQUE
columns are created by default - Obviously, non-unique indexes allow duplicate values
- Indexes are implicitly unique when created on columns with a
- "Rules of Thumb" for when to create indexes
- Create for foreign key columns
- Don't create for small tables (fewer than 50 rows)
- With large tables, create only when queries on the column(s) are searching for fewer than roughly 15% of rows
- Indexes are usually helpful on columns used in table joins
- Indexes are useful on columns often used together in the
WHERE
clause ofSELECT
statements - Indexes slow down DML operations which involve indexed columns
- This is because index often has to be updated & resorted, and this often involves disk read & writes
- Ergo, minimize indexes on tables that are frequently updated
CREATE INDEX
statements- Syntax/example (nonclustered index):
CREATE INDEX myindex ON myschema.sometable(columnfoo);
- Syntax/example (clustered index):
CREATE CLUSTERED INDEX myindex ON myschema.sometable(columnfoo);
- Syntax/example (nonclustered index with unique constraint & sort order):
CREATE UNIQUE INDEX myindex ON myschema.sometable
(columnfoo DESC, columnbar ASC);
- Syntax/example (nonclustered index):
- General
- Views
- General
- Often described as "virtual tables"
- Unlike tables, views do not occupy disk space
- "View definitions are stored in RDBMS as compiled queries that dynamically populate data to be used as virtual tables for users' requests." (Kriegel & Trukhnov: 2nd Ed., p. 120)
- Typical uses:
- Combining data from multiple tables into a more usable format
- Enforce security rules by exposing only horizontal and/or vertical slices of data
CREATE VIEW
statement/syntax:
CREATE VIEW <view_name> [(column1_name,…)]
AS <query_expression>
[WITH [CASCADED ¦ LOCAL] CHECK OPTION]- Column names
- If the
column_name
list is omitted, columns will be named based on theSELECT
statement - Columns must be named in either of the following cases:
- There would be ambiguity (from 2 or more columns having the same name)
- The
SELECT
statement includes any kind of computed value but has not aliased the resulting column
- If the
- The
SELECT
statement & updatable views- Restrictions on embedded
SELECT
statement (these are quite limited):- Cannot use the
ORDER BY
clause - The view definition cannot be circular - either directly or indirectly
- Cannot use the
- Updatable views
- Updatable views are those which can be used in DML statements
- Views can be, but do not have to be, updatable
- General rules for a view to be updatable:
- One-to-one correspondence between rows of the view & rows of the underlying, physical table
- One-to-one correspondence between columns of the view & columns of the underlying, physical table
- Note: The converses need not hold, as rows in the base table are often filtered out (& columns omitted) when creating views
- Specific restrictions on updatable views:
- The
query_expression
does not contain any table joins - i.e., the view refers to one & only one table or view - If one view is based on another, the underlying view can only refer to 1 table for the outer view to be updatable
- All of the underlying table's mandatory columns (i.e.,
NOT NULL
columns) are included in the view definition - The underlying query does not contain any set operations - i.e.,
UNION, EXCEPT,
orINTERSECT
- The
DISTINCT
keyword is not allowed - No aggregate functions or expressions can be included in the
query_expression
- The underlying query cannot have a
GROUP BY
clause
- The
- Restrictions on embedded
- View constraints
- Not allowed
- However, the
CHECK OPTION
is another way to skin that cat (at least partially)- The
CHECK OPTION
clause can only be used with updatable views - This requires that any DML statements executed on the view has no effects other than those which are visible though the view
- Options:
CASCADED
(default) - if a view is based on other views the underlying views are also checkedLOCAL
-CHECK OPTION
is not run against underlying view(s)
- The
- Column names
- Creating complex views (see Kriegel & Trukhnov: 2nd Ed., pp.127 - 9, for examples)
- General
- Aliases & synonyms (not part of SQL:2003 standards)
- Schemas
- General
- Defined in SQL:2003 as a named group of related objects
- Useful when you have circular references - i.e., two tables with foreign keys referring to the other table
CREATE SCHEMA
statement- Note: You can grant privileges on the objects within a schema when creating the schema
- Syntax:
CREATE SCHEMA
{<schema_name> ¦
AUTHORIZATION <authorization_id> ¦
<schema_name> AUTHORIZATION <authorization_id>
}
[DEFAULT CHARACTER SET <character_set>]
[<schema_path_specification>]
[<create_object_statement> ¦
<grant_privilege_statement>…] - The
AUTHORIZATION <authorization_id>
clause is used when the schema creator does not own the objects within the schema - Can obviously create a default character set different from the database default character set
- Objects creatable as part of a schema:
- Tables
- Views
- Domains*
- Assertions*
- Character sets*
- Collations*
- Translations*
- Triggers
- Transforms
- Schema routines
- Sequences
- User-defined objects
- *Note: though part of SQL:2003 standards, these operations are not in any of the Big Three databases (at least as of 2008, per Kriegel & Trukhnov: 2nd Ed.)
- General
- Sequences
- General
- A database object similar to that of an identity (column)
- Difference:
- An identity is tied to a table column
- A sequence, however, is an independent entity
- Multiple users can generate numeric values from a single sequence & use those values for different purposes
- Typical use patterns:
- To generate primary key values for one or more tables
- Tend to be most useful within context of procedural programs
- Absent the use of procedural programs one typically creates a one-to-one relationship between a sequence and a table (again, specifically for the table's primary key)
- Sequence generators
- Internal sequence generators:
- Anonymous & created behind the scenes as part of other database objects
- Identity columns are associated with internal sequence generators
- External sequence generators
- Syntax:
CREATE SEQUENCE <sequence_name>
[AS <data_type>]
[START WITH <start_value>]
[INCREMENT BY <increment_value>]
[MAXVALUE <max_value> ¦ NOMAXVALUE]
[MINVALUE <min_value> ¦ NOMINVALUE]
[CYCLE ¦ NOCYCLE] - Optional clauses can be in any order
<increment_value>
can be negative- Use
CYCLE
if you want your sequence generator to start over after reaching itsMAXVALUE
(orMINVALUE
for a descending sequence) MINVALUE
for ascending sequences, &MAXVALUE
for descending sequences, only make sense if the sequences cycle- When a sequence cycles it doesn't start over at the
START WITH
value, but at the appropriateMAXVALUE/MINVALUE
- Syntax:
- Internal sequence generators:
- General
- Other SQL:2003 & implementation-specific objects
- General
- SQL:2003 defines any number of objects not implemented by any of the Big Three
- Conversely, each vendor implements unique objects
- Domains (not implemented by any of the Big Three)
- An alternative to a data type, to be used when defining table columns
- Can optionally specify:
- Default value
- Collation
- Set of constraints
- Syntax:
CREATE DOMAIN <domain_name> [AS] <datatype>
[DEFAULT <default_value>]
[<constraint_definition>…]
[COLLATE <collation_name>]
- Character sets
- Cannot create a new character set - can only copy & rename an existing one
- Syntax:
CREATE CHARACTER SET <char_set_name> [AS]
GET <char_set_specification> [<collate_clause>]
- Collations
- Cannot create a new collation. Can only:
- Copy & rename an existing one
- Change its padding option
- Syntax:
CREATE COLLATION <collation_name>
FOR <char_set_specification>
FROM <existing_collation_name>
[NO PAD ¦ PAD SPACE]
- Cannot create a new collation. Can only:
- Tablespaces & filegroups (not in SQL:2003)
- General
- Tables
- Altering & Destroying RDBMS Objects
- Tables
ALTER TABLE
statement- Syntax:
ALTER TABLE <table_name> {
[ADD [COLUMN] <col_definition>] ¦
[ALTER [COLUMN] <col_name>] {
SET DEFAULT <default> ¦
DROP DEFAULT ¦
ADD <scope_clause> ¦
DROP SCOPE <drop_behavior> ¦
<alter_identity_col_option>
} ] ¦
[DROP [COLUMN] <col_name> RESTRICT ¦ CASCADE] ¦
[ADD <table_constraint>] ¦
[DROP CONSTRAINT <constraint_name>
RESTRICT ¦ CASCADE]
}; - Note: The scope-related options apply only to reference tables
- Essentially, this code allows you to:
- Add or delete a table column
- Add or delete a default value for a column
- Drop a table constraint
- Syntax for altering identity columns (the same as for creating one):
[
[START WITH <integer>]
[INCREMENT BY <integer>]
[MINVALUE <integer>]
[MAXVALUE <integer>]
[CYCLE ¦ NO CYCLE]
]
- Syntax:
DROP TABLE
statement- General
- Statement removes both table & related indexes on columns from physical storage
- The following (where related) are removed from the database data dictionary:
- Table definitions
- Index definitions
- Integrity constraints
- Triggers
- Views (though, apparently, this is not adhered to by the Big Three)
- Any other objects that reference the table
- Note: as with any DDL statement,
DROP TABLE
statements are irreversible- The statement is committed immediately
- The statement cannot be rolled back
- Syntax:
DROP TABLE <tbl_name> [{CASCADE ¦ RESTRICT}]
- When specifying the
CASCADE
option:- All dependent objects (views, constraints, triggers, etc.) on the table will be removed
- Any rows in other tables referencing the dropped table will be deleted!
- General
- Indexes
- General
- Generally one doesn't change indexes because they are invisible to users
- If and when you do modify an index you often simply drop and recreate it
ALTER INDEX
statement (not part of SQL:2003 standards)DROP INDEX
statement- Removes the index from the database information schema
- Cannot drop indexes used to implement
PRIMARY KEY
orUNIQUE
constraints - To modify a
PRIMARY KEY
orUNIQUE
index must useALTER TABLE…DROP CONSTRAINT
statement
- General
- Views
ALTER VIEW
statement - syntax is implementation-specificDROP VIEW
statement (dependent objects become invalid)
- Aliases & synonyms (these are not part of SQL:2003 standards)
- Schemas
ALTER SCHEMA
statement - syntax is implementation-specific- Not part of SQL:2003 standards
- Might be part of SQL:2008 standards
DROP VIEW
statement (dependent objects become invalid)- Syntax:
DROP SCHEMA <schema_name> [CASCADE ¦ RESTRICT]
RESTRICT
keyword means the schema has to be emptyCASCADE
keyword means all schema objects will be dropped
- Syntax:
- Sequences
ALTER SEQUENCE
syntax:
ALTER SEQUENCE <sequence_name>
[START WITH <start_value>]
[INCREMENT BY <increment_value>]
[MAXVALUE <max_value> ¦ NOMAXVALUE]
[MINVALUE <min_value> ¦ NOMINVALUE]
[CYCLE ¦ NOCYCLE]DROP SEQUENCE
statement- Syntax:
DROP SEQUENCE <seq_name> [CASCADE ¦ RESTRICT]
RESTRICT
option means the sequence will not be dropped if referenced within the database by any- Triggers
- Routines
- Syntax:
- Other implementation-specific objects
- Domains
ALTER DOMAIN
syntax:
ALTER DOMAIN <domain_name>
[SET <default clause> ¦ DROP DEFAULT ]
[ADD <domain_constraint> ¦
DROP_CONSTRAINT <constraint_name>,… ]DROP DOMAIN
syntax:
DROP DOMAIN <domain_name> [CASCADE ¦ RESTRICT ]
- Character sets
- Character sets cannot be modified
DROP CHARACTER SET
syntax:
DROP CHARACTER SET <char_set_name>
- Collations
- Collations cannot be modified
DROP COLLATION
syntax:
DROP COLLATION <coll_name> [CASCADE ; ¦ RESTRICT ]
- Domains
- Tables
- Creating RDBMS Objects
- Data Manipulation & Transaction Control
- Data Manipulation Language (i.e., DML)
INSERT
: Populating tables with data- General
- Two 'avenues' for inserting data:
- Directly into a table
- Into an updatable view
- Syntax:
INSERT INTO <table_or_view_name>
[(<column_name1>,…)]
{ {VALUES (<literal> ¦
<expression> ¦
NULL ¦
{<select_statement>} ¦
{DEFAULT VALUES} } - Syntax (a variation that will work with all vendors):
INSERT INTO <table_or_view_name>
[(<column_name1>,…)]
{ {VALUES (<literal> ¦
<expression> ¦
NULL ¦
DEFAULT),… } ¦
{<select_statement>} }
- Two 'avenues' for inserting data:
- Common
INSERT
statement clauses- General
- Records typically added to a table one at a time with the
INSERT
statement - Column names
- Can be omitted completely if you are inserting to all columns in a table
- Including all column names allows you to specify order of the values
- Omitted column names will be either:
- Populated with a
NULL
value, or - Populated with a default value
- Populated with a
- Must include all
NOT NULL
columns, unless:- The column is populated by default (e.g., AutoNumber columns)
- The column has a default value
- Note: Failure to adhere to this will of course generate a RDBMS error
- Records typically added to a table one at a time with the
- Syntax
- Syntax/example (inserting values for specified columns):
INSERT INTO Product
(
productid,
name,
description,
manufacturerid,
price,
status
)
VALUES
(
123,
'Foo bar',
'Widget',
'456z',
NULL,
DEFAULT
) - Note: As shown, you can also explicitly specify
NULL
&DEFAULT
values - When inserting values for all columns
- You can omit column names in this case
- However, must make sure values in your
VALUES
are in the right order
- Syntax/example (inserting values from another table):
-- Typically done to archive data
-- Tables have identical structures
-- Here archiving shipments older than 180 days
INSERT INTO ShipmentArchive
SELECT *
FROM shipment
WHERE DATEDIFF(day, shipmentdate, SYSDATE) > 180;
- Syntax/example (inserting values for specified columns):
INSERT
statement & integrity constraints- Some vendors will perform some implicit conversions
- Beyond that, though, the
INSERT
command will fail - In large systems you may try to insert thousands of rows at a time, and a single bad entry will cause the entire batch to fail
- Oracle provides a DML error logging feature
- With other vendors use procedural SQL to deal with data errors
- General
- General
UPDATE
: Modifying table data- General
- Two 'avenues' for updating data:
- Directly in a table
- Through an updatable view
- Syntax (generic):
UPDATE <table_or_view_name> [ [AS] <correlation_name>]
SET <column_name1> = { <literal> ¦
<expression> ¦
(<single_row_select_statement>) ¦
NULL ¦
DEFAULT
},…
[WHERE <predicate>] - In
SET
clause you can include multiple column/value pairs (separating by a comma) - However, can update only one table at a time
- Two 'avenues' for updating data:
- Examples
- Syntax/example (updating a single column of a single row):
UPDATE Product
SET price = 99.99
WHERE productid = 1234 - Syntax/example (updating multiple columns):
UPDATE Product
SET price = 99.99,
productname = 'Foo Bar II'
WHERE productid = 1234 - Syntax/example (updating single column in all rows):
-- Simply omit WHERE clause
UPDATE Product
SET price = price*1.05 - Updating columns using a single-row subquery
- General
- Within your
SET
statement include aSELECT
statement to set the value of a column/value pair - That
SELECT
statement must return no more than 1 row
- Within your
- Syntax/example (deriving the assignment value from another value):
UPDATE OrderHeader
SET paytermsid =
(SELECT paytermsid
FROM PaymentTerm
WHERE paytermscode = 'N12345')
WHERE orderheaderid = 998765 - Update with correlated subquery
- A correlated subquery is where a subquery refers to a value from the outer query
- Syntax/example:
UPDATE OrderHeader
SET paytermsid =
(SELECT paytermid
FROM PaymentTerm as pt
JOIN Customer as c
ON (pt.paytermid = c.paytermid)
WHERE OrderHeader.customerid = c.customerID) - Essentially, you do this by using a
JOIN
to specify your column value - You
JOIN
the table you are updating with the tables assigned in the assignment subquery - Note: this effectively overrides the "single-row" rule
- General
UPDATE
statement & integrity constraints- Integrity constraints function similarly to using the
INSERT
statement - Difference arises from the effect on child tables from
ON UPDATE CASCADE
orON UPDATE SET NULL
constraints
- Integrity constraints function similarly to using the
- Syntax/example (updating a single column of a single row):
- General
DELETE
: Removing data from tables- General
- Can delete directly from a table or from an updatable view
ON DELETE
referential integrity constraints of course kick in- Syntax:
DELETE FROM <table_or_view_name>
[WHERE <predicate>]
- Common
DELETE
statement clauses- General
DELETE
statements are very simple because there is notSELECT
clause- Obviously, it is critical to take care constructing the
WHERE
clause
DELETE
statement & integrity constraints- Integrity constraints are not as much of a factor in
DELETE
statements as they are inINSERT
orUPDATE
statements - For example,
PRIMARY KEY, UNIUQE,
andNOT NULL
constraints are not a concern withDELETE
statements - Referential integrity constraints, however, are very much a consideration
- Beware of the
ON DELETE CASCADE
option, as this constraint causes child rows to be deleted when a parent row is deleted! - The
ON DELETE NULL
option is also very dangerous (this sets the value in a child's foreign key column toNULL
)
- Integrity constraints are not as much of a factor in
- Syntax/example (using subqueries in a DELETE statement):
DELETE FROM OrderHeader
WHERE customerid =
(SELECT c.customerid
FROM Customer as c
WHERE c.name = 'RCP Consulting')
- General
- General
MERGE
: combiningINSERT, UPDATE,
&DELETE
in one statement- Syntax (generic):
MERGE INTO [<qualifier>.]<target_table>
USING [<qualifier>.]<source_table> ON (<condition>)
WHEN MATCHED THEN
UPDATE SET {<column> = {<expression> ¦ DEFAULT},…}
WHEN NOT MATCHED THEN
INSERT [(<column>,…)] VALUES (<expression> ¦ DEFAULT),…;
- Syntax (generic):
TRUNCATE
statement- Not part of ANSI SQL
- Oracle & MSSS only
- Sessions, Transactions & Locks
- Sessions
- Session = environment in which (among other things) transactions & locks take place
- Session begins with connection to a server - i.e., opening a session
- Changes to the settings apply only to the existing session
- If you open multiple instances of, say, SQLCMD, on a single machine each instance represents its own session
- Connection & session management statements:
- Statements for connecting & security
CONNECT TO
&DISCONNECT
SET CONNECTION
(to select from multiple available connections)SET SESSION AUTHORIZATION
(to set session user identifier)SET ROLE
- Statements relating to
<preparable statement>
s- Conditions:
- Prepared in current SQL session by either:
- An
<execute immediate statement>
- A
<prepare statement>
- An
- And in
<direct SQL statement>
s that are invoked directly
- Prepared in current SQL session by either:
- Statements:
SET CATALOG
(for unqualified<schema name>
s)SET SCHEMA
(for unqualified<schema qualified name>
s)SET NAMES
(for setting default character set name for<character string literal>
s)SET PATH
(for setting the path to determine the subject routine of<routine invocation>
s with unqualified<routine name>
s)
- Conditions:
- Other
SET SESSION CHARACTERISTICS AS
(to set one or more characteristics of the current session)SET TRANSFORM GROUP
(used for mapping values of user-defined types to predetermined data types for a group of transform functions)SET COLLATION
(affects one or more character sets)
- Statements for connecting & security
- Orphaned sessions
- Occurs when a client application terminates abruptly
- Generally the vendor applications will notice this situation after some specified interval and execute the proper clean-up
- Orphaned sessions can also be resolved manually by the DBA
- Transactions
- Transaction defined
- Provides granularity to concept of a session
- Represents logical units of work within a session
- Insures that multi-step operations are processed as a single unit (i.e., the entire transaction)
- Transactions continue until either a
COMMIT
orROLLBACK
statement is issued - Relevant T-SQL statements (see )
BEGIN DISTRIBUTED TRANSACTION
(across multiple servers)- Use to work with multiple servers
- Beneath the covers you are working with MS DTC
BEGIN TRANSACTION
(can set transaction characteristics here)COMMIT TRANSACTION
COMMIT WORK
ROLLBACK TRANSACTION
ROLLBACK WORK
SET TRANSACTION
- set characteristics of next transactionSAVE TRANSACTION
- ACID test for transactions
- Atomicity - either all of the changes are made or none are
- Consistency - upon either completion or rollback of a transaction all of the data involved must:
- Be left in a consistent state
- Database integrity cannot be compromised
- Isolation
- One transaction should not be aware of modifications made by a second transaction (unless and until, of course, that second transaction has been committed)
- Note: you can modify this default behavior with differing isolation levels
- Durability - once a transaction has been committed its results must remain in place
- Implicit and explicit transactions
- An implicit transaction is the default - transactions are automatically started with certain SQL statements
- An explicit transaction is started with a
BEGIN TRANSACTION
statement
- Transactions
COMMIT
&ROLLBACK
- Savepoints
- Adds granularity to transaction processing
- Allows you to set a named point within the transaction (which you typically use to indicate that some important milestone in a query has been completed)
- If there are ensuing errors you can then rollback to your savepoint (i.e., you do not have rollback all of your processing)
- Savepoints are released once an entire transaction has been committed
- Savepoint names must be unique within a given transaction (if a duplicate name is used the initial savepoint is destroyed)
- Distributed transactions
- Defined: transactions that involve more than one database
- These are very complex and generally involve two-phase
COMMIT
syntax
- Savepoints
- Transaction isolation levels
- Isolation levels refer to the ability of a transaction to see outside its own scope (i.e., data modified by another transaction)
- Levels:
READ UNCOMMITTED
- Lowest isolation level
- Permits dirty reads - i.e., the ability to see uncommitted data
- Neither issues nor honors locks
READ COMMITTED
- Specifies that shared locks will be held while data are read
- Dirty reads are not permitted
- Does allow phantom reads - i.e., when row numbers change between reads
REPEATABLE READ
- No changes are allowed for the data selected by a query
- However, phantom rows may appear
SNAPSHOT ISOLATION
- More flexible that
SERIALIZABLE
isolation - All database reads within transaction see a snapshot of db from moment when transaction began
- In other words, snapshot = latest committed values
- Before commiting transaction, verifies that no other pending updates exist that would conflict
- More flexible that
SERIALIZABLE
- Highest isolation level
- Puts a lock on the whole dataset
- No modifications from the outside are allowed until the end of the transaction
- Transaction defined
- Locks
- General
- Deals with concurrency
- Technically, locking is part of neither SQL nor SQL:2007
- Concurrency modes
- Optimistic
- Assumes that more than one transaction working on the same set of data, while possible, is unlikely
- Checks for potential conflicts when committing changes
- Resolves conflicts by resubmitting changes
- Pessimistic
- Expects conflicts from the very beginning
- Therefore locks all resources that the transaction intends to use
- Obviously, pessimistic locking can significantly slow down a database
- Locks are used to implement pessimistic transactions
- Optimistic
- General locking modes
- Shared
- Exclusive
- Exact variations and implementation varies by vendor
- Can also lock specific objects - e.g., rows versus tables
- Deadlocks
- Occurs when:
- Two or more sessions are waiting to acquire a lock on a shared resource
- Neither session can proceed because a second session has a lock on some other resource required by the first session
- RDBMSs usually handle these situations with specific algorithms
- As a last resort the DBA can manually resolve deadlocks
- Occurs when:
- General
- Sessions
- Data Manipulation Language (i.e., DML)
- Retrieving & Transferring Data
- Single-table queries
- The
SELECT
statement (i.e., DQL) & its clauses- General
SELECT
is the only statement in DQL- Single-Table
SELECT
statement syntax:
SELECT [DISTINCT]
{
[<qualifier>.]<column_name> ¦
* ¦
<expression> ¦
<pseudocolumn>
[AS <column_alias>]
},…
FROM
{
<table_or_view_name> ¦
<inline_view>
[[AS] <table_alias>]
}
[WHERE <predicate>]
[GROUP BY [<qualifier>.]<column_name>,…
[HAVING <predicate>]]
[ORDER BY {
<column_name> ¦
<column_number>
}
[ASC ¦ DESC],…
]; - Although
SELECT
clause appears 1st in a query, it is processed after all other clauses except forORDER BY
- Delimiting an object name
- When it must be done
- Contains embedded spaces or special characters
- Starts with a digit
- Is a reserved keyword
- Delimit with (double) quotation marks
- ANSI SQL: (double) quotation marks
- T-SQL: either quotation marks or square brackets (typically preferred)
- No harm in delimiting all object names, though it clutters the code
- Syntax/example:
dbo.Employee."Last Name"
(could also do"dbo"."Employee"."Last Name"
, etc.)
- When it must be done
- Note: Clauses below are described in the logical order in which the db engine processes them
- The
FROM
clause- Selecting from tables & views
- In some sense the only thing you can select from is a table
- A view, after all, is simply a query of a table
- Schemas
- Best Practice: always schema-qualify object names - in this case tables - in code
- When you don't
- Run risk of db engine picking wrong object
- Schema has to be resolved one way or another, & if you do not specify schema you incur (minor) cost of forcing db engine to resolve the schema
- Using aliases in a
FROM
clause- When established all other clauses in the overall
SELECT
query should utilize the alias - Notes:
- The aliases used in a
FROM
clause differ from those established with aCREATE ALIAS
statement - An alias need not be used
- Table aliases can be utilized throughout the SELECT statement - i.e., in WHERE, GROUP BY, etc.
- The aliases used in a
- Syntax/example (and, yes, you can mix as match as follows):
SELECT firstname,
Student.lastname,
s.studentid
FROM FaberCollege.Student [AS] s - Typically, though, you only use table aliases in multi-table queries
- When established all other clauses in the overall
- Using subqueries in a FROM clause (aka, inline views)
- The object of your FROM clause can itself be another entire SELECT statement
- Syntax/example:
SELECT s.firstname,
s.lastname,
s.studentid
FROM (SELECT fname as firstname,
lname as lastname,
ssn as studentid
FROM FaberCollege.Student
WHERE HomeState = 'NJ') AS s - Note: Inline views, as opposed to
VIEWS
, exist only for the duration of the query - If you alias a column name within an inline view then an outer query which references that column can only do so by using that alias
- Selecting from tables & views
- The
WHERE
clause- General
- You are narrowing the number of rows you to look at
- Often referred to as setting horizontal limits
- Relies on predicate logic
- That is, the criteria must all evaluate to
True, False
, orUnknown
- Only records evaluating to
True
are accepted
- That is, the criteria must all evaluate to
- Operators used
- Comparison operators - i.e.,
=, >, <,…
- Compound operators - i.e.,
AND
&OR
BETWEEN
- Syntax:
BETWEEN
xAND
y BETWEEN
clause is inclusive
- Syntax:
IN
(i.e., set membership test)- Essentially, you use this to replace numerous
OR
operators - Must include the set inside parentheses
- Can use a subquery to define your
IN
set - Note: because the contents of an
IN
are being compared to a single column all members of theIN
set must be of the same data type
- Essentially, you use this to replace numerous
- The
NOT
operator
- Comparison operators - i.e.,
- Indexes
- Db engines typically look to use indexes to access/filter the data
- Generally this gives much faster results than doing a full table scan
- The
IS NULL
operator- Remember that comparison operators tend to choke on
NULL
values - To check for
NULL
or non-NULL
values the syntax is:WHERE xyz IS NULL
WHERE xyz IS NOT NULL
- How to throw an error with
NULL
!-
WHERE xyz = NULL
-
WHERE xyz NOT IS NULL
-
- Remember that comparison operators tend to choke on
- The
COALESCE
vs.ISNULL
functions [T-SQL]- General
COALESCE
- Returns the first non-null expression among its arguments
- Accepts more than 2 inputs
- Is a standard ANSI SQL function
ISNULL
- Replaces
NULL
with a value you specify - Can only handle a single
NULL
value - T-SQL-specific
- Replaces
- Typed vs. untyped
NULL
- The
NULL
literal is where you have keyed in "NULL" as an input- Example #1: (using the untyped NULL literal)
INSERT INTO Employee
(
firstname,
lastname,
suffix
)
VALUES
(
'William',
'White',
NULL
) - Example #2: (using the untyped NULL literal)
--NOTE: this will generate an error.
SELECT COALESCE(NULL, NULL)
- Example #1: (using the untyped NULL literal)
- The typed
NULL
literal is where you have specified the data type for your "NULL" input- Example #1: (using a typed NULL literal)
-- Snippet
DECLARE
@x AS INT = NULL,
@y AS INT = 1,
@z AS INT = 2 - Example #2: (using a typed NULL literal)
-- Will return NULL as an integer.
SELECT COALESCE(CAST(NULL AS INT), NULL);
- Example #1: (using a typed NULL literal)
- The
- Data type precedence
- When an operator works with different data types it first converts data type of lower precedence into that of the parameter with the higher precedence
- An error is returned if this conversion cannot be done implicitly
- Once the conversion is made the result of the operation will be of the same data type
- List - highest to lowest
- user-defined data types
- sql_variant
- xml
- datetimeoffset
- datetime2
- datetime
- smalldatetime
- date
- time
- float
- real
- decimal
- money
- small money
- bigint
- int
- smallint
- tinyint
- bit
- ntext
- text
- image
- timestamp
- uniqueidentifier
- nvarchar (including nvarchar(max))
- nchar
- varchar (including varchar(max))
- char
- varbinary (including varbinary(max))
- binary
- Data type of returned expression
COALESCE
- Corresponds to input with highest data type precedence
- If all inputs are untyped
NULL
s you get an error
ISNULL
- Data type of the first input
- If the first input is an untyped
NULL
the data type of the second input is returned - If both inputs are an untyped
NULL
anINT NULL
is returned
- Examples (data types of the same family):
DECLARE
@x as VARCHAR(3) = NULL,
@y as VARCHAR(5) = '12345';
SELECT
COALESCE(@x, @y) AS COALESCExy,
COALESCE(@y, @x) AS COALESCEyx,
ISNULL(@x, @y) AS ISNULLxy,
ISNULL(@y, @x) AS ISNULLyx;
-- NOTE: VARCHAR(5) has a higher precedence than VARCHAR(3)
-- Results
-- Both COALESCE expressions return the varchar '12345'
-- ISNULLxy returns varchar '123' - i.e., truncated to VARCHAR(3)
-- ISNULLyx returns varchar '12345' - Results when data types are from different families
- Example #1:
-- INT has a higher precedence than a character string
-- This will fail, because char string cannot be
-- implicitly converted to an INT
SELECT COALESCE('abc', 1); - Example #2:
-- This will return 'abc'
-- ISNULL uses data type of the first input
SELECT ISNULL('abc',1) - Example #3:
-- Will generate an error
-- At least 1 argument must be other than untyped NULL
SELECT COALESCE(NULL, NULL); - Example #4:
-- Will generate NULL cast as an INT
SELECT ISNULL(NULL, NULL);
- Example #1:
- Nullability of expression
- This is a consideration when executing a
SELECT INTO
statement SELECT INTO
creates a new table- Determining the nullability of an input
- A numeric or string argument is non-nullable
- A column reference (name) is considered nullable
- Determining whether columns created with
SELECT INTO
are nullable- With
COALESCE
- If all expressions are non-nullable then the column will be defined as
NOT NULL
- Otherwise, the created column is nullable
- If all expressions are non-nullable then the column will be defined as
- With
ISNULL
- If one of the two arguments is non-nullable then the resulting column will be non-nullable
- Otherwise, the created column is nullable
- With
- This is a consideration when executing a
- Use with subqueries
- Wherever possible go with
ISNULL
vs.COALESCE
ISNULL
is more efficient in a subquery- You can in fact get some funky results using
COALESCE
- Under the hood of
COALESCE
- Per standard SQL,
COALESCE
is translated into aCASE
statement - Specifically:
COALESCE(arg1, arg2)
-- Becomes:
CASE
WHEN arg1 IS NOT NULL THEN arg1
ELSE arg2
END
- Per standard SQL,
- Clustered index scan
- Will occur twice with
COALESCE
- 1st read: to see if the result is
NULL
- 2nd read: to obtain the non-null result
- 1st read: to see if the result is
- With
ISNULL
will only occur once - Note: for each index scan there are then a number of logical reads
- Will occur twice with
- Wherever possible go with
- Atomicity vs. Isolation
- You can get strange results with
COALESCE
- Seems to be a special case of a table being dropped by another user whilst you run
INSERT INTO
- Behavior seems to violate atomicity of
SELECT
statement - See Ben-Gan's April 16, 2013 article COALESCE vs. ISNULL, in ITPro Today for a discussion of this issue
- You can get strange results with
- General
- Using subqueries in a
WHERE
clause- General
- This general approach is an alternative to using an inner join
- Subqueries execute before the calling queries
- Types of subqueries
- Scalar - your subquery returns a single value
- Syntax/example (adapted from Kriegel & Trukhnov: 2nd Ed., p. 281):
SELECT ordernumber,
orderdate
FROM Sales.Order
WHERE customerid =
(SELECT customerid
FROM Sales.Customer
WHERE customername = 'RCP Consulting, LLC') - Note: a query like this fails if the subquery returns multiple values
- Syntax/example (adapted from Kriegel & Trukhnov: 2nd Ed., p. 281):
- Non-scalar subqueries
- Syntax/example (note use of
IN
keyword):
SELECT phonenumber,
phonetype
FROM HR.Phone
WHERE salesmanid IN
(SELECT salesmanid
FROM Sales.Salesman
WHERE salesmancode BETWEEN '07' AND '10') ANY
&ALL
keywords- Can use when the outer query is performing a comparison to multiple values returned by a subquery
- Assume a view which contains order totals from a single company
- Syntax/example (adapted from Kriegel & Trukhnov: 2nd Ed., pp. 282‑3):
-- "> ANY" effectively "> MIN"
SELECT v.customername,
v.totalprice
FROM vwCustTotals v
WHERE v.totalprice >
ANY (SELECT totalprice
FROM vwCustTotalsAcme)
ORDER BY totalprice ASC - Syntax/example (adapted from Kriegel & Trukhnov: 2nd Ed., pp. 283‑4):
-- "> ALL" effectively "> MAX"
SELECT v.customername,
v.totalprice
FROM vwCustTotals v
WHERE v.totalprice >
ALL (SELECT totalprice
FROM vwCustTotalsAcme)
ORDER BY totalprice ASC - Syntax: Insert
ANY
orALL
after the comparison operator but before the subquery - Another way to skin this cat is to omit
ANY
and/orALL
and instead useMAX
&MIN
functions within the subquery
- Syntax/example (note use of
- Scalar - your subquery returns a single value
- Nested subqueries
- Quite do-able
- However, these tend to be resource-intensive, so use judiciously
- Indexes
- Utilized by the
WHERE
clause - Reminder: with 3-value predicate logic only logical expressions returning
True
are returned
- Utilized by the
- General
- General
- The
GROUP BY
clauseGROUP BY
is typically used in conjunction with aggregate functions (e.g.,SUM, AVG,
etc.)- When specifying > 1 field in the
GROUP BY
clause you ultimately produce 1 row for each unique combination of those fields - Example:
- You have employees from 5 states working at your company
- Your employees are born across 15 separate years
- The following query will return up to 75 rows (5 X 15):
SELECT
state,
YEAR(bdate) AS "Birth Year",
COUNT(*) AS "Nmbr Employees"
FROM HR.Employee
GROUP BY state, YEAR(bdate)
- Subsequent clauses - e.g.,
HAVING, SELECT, & ORDER BY
- These will/must operate on the grouped results, not on individual rows within the queried table
- This in turn means: any expression used within those subsequent clauses must return only a scalar - i.e., single value - per group
- Implications
- Fields & expressions used in the
GROUP BY
clause can automatically be used in subsequent SQL phrases- Rationale: the
GROUP BY
clause has already guaranteed uniqueness on those fields and/or expressions - Note: this is why we could use
e.state
&YEAR(e.bdate)
inSELECT
phrase of above query
- Rationale: the
- Handling fields not included in the
GROUP BY
clause- Such fields can only appear as inputs to aggregate functions such as
COUNT, SUM, AVG, MIN
, etc. - This works because such aggregate functions are scalar
- Such fields can only appear as inputs to aggregate functions such as
- Fields & expressions used in the
- Aggregate functions
- You can use
DISTINCT
inside all aggregate functions NULL
values- Ignored by all aggregate functions except
COUNT(*)
COUNT(*)
ignoresNULL
values because it is giving you a count of the rows- However, a command such as
COUNT(phonenumber)
ignoresNULL
s in thephonenumber
column
- Ignored by all aggregate functions except
- You can use
- The
HAVING
clauseHAVING
clause is only used in conjunction withGROUP BY
clause- Works on results after they have been grouped
- As with
WHERE
clause relies on predicate logic- Statements must evaluate to
True, False
, orUnknown
- Only groups evaluating to
True
are returned
- Statements must evaluate to
- Because you are working on groups you can employ aggregate functions
- Syntax/example (adapted from Ben-Gan, 3rd Ed., p. 36):
SELECT empid, YEAR(orderdate) as orderyear
FROM Sales.Order
WHERE custid = 123
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1 - Note: the column used in the
HAVING
clause does not have to appear in theSELECT
clause
- The
SELECT
clause- Multi-column
SELECT
statements- Can cherry-pick columns to
SELECT
- This query returns a set of sets; each column is an individual set of values
- You can, of course, include a column more than once in a query
- Selecting all columns
- Syntax:
SELECT *
- Columns are returned in the order in which they reside in the underlying table
- Syntax to an select all columns, & then add/repeat a column:
SELECT *, columntorepeat
- Syntax:
- Selecting distinct values
- Syntax/example:
SELECT DISTINCT YEAR(bdate),
residencestate
FROM Employee - Note:
DISTINCT
refers to entire row, not just to a single column
- Syntax/example:
- Can cherry-pick columns to
- Literals, functions, & expressions/calculated values
- You can include "constant" values that appear in every row of your output
- You create/define these values as literals, results of functions, etc.
- These are simply included as though they were columns in regular
SELECT
statements - Dummy tables
- Oracle
- Oracle requires use of the
FROM
clause inSELECT
statements - Dummy tables used when a value does not exist until the moment you call it (i.e., because the value is the result of a function)
- When you use the
FROM
clause SQL requires that you provide something as the object of that clause - One uses a dummy table for this
- Note: dummy tables are not a part of the SQL standard
- Oracle requires use of the
- SQL Server
- MSSS does not requires the use of the
FROM
clause inSELECT
statements - Syntax/example:
-- This is perfectly legit in SQL Server
SELECT 2 + 2 AS mysimplesum - Ergo, dummy tables are neither needed nor provided in SQL Server
- MSSS does not requires the use of the
- Oracle
- Column aliases
- Use whenever you want to:
- 'Rename' a column - e.g.,
SELECT empid AS EmployeeID
- Provide a heading for a constant value or calculated column - e.g.,
SELECT YEAR(bdate) AS "Year Born"
- Technically, you do not have to provide column aliases for these columns - results will still appear
- However, if you do not:
- Data obviously a little harder to read/understand
- The unnamed columns then cannot be referred to in any outer
SELECT
statement - Your results can no longer be considered a relation
- 'Rename' a column - e.g.,
- The
AS
keyword- Use is optional
- Best Practice, however, is always to use it
- Makes query clearer
- Risk of not habitually using
AS
keyword: if you miss a comma between field names inSELECT
phraseCOL2
gets interpreted as alias forCOL1
, & you will find that hard to spot
- Note: because of logical processing order remember that column aliases are not available in the following clauses:
FROM
WHERE
GROUP BY
HAVING
- Use whenever you want to:
- Using subqueries in a
SELECT
clause- Can be done
- Syntax/example (adapted fromKriegel & Trukhnov: 2nd Ed., pp. 271‑2)
SELECT productnumber,
price,
(SELECT taxrate
FROM SalesTax
WHERE state = 'NJ') AS njtaxrate,
price *
(SELECT taxrate
FROM SalesTax
WHERE state = 'NJ')/100 as totalsalestax
FROM Product
- Multi-column
- The
ORDER BY
clause (& some order-related filtering)- General
ASC
(the default) &DESC
keywords come after the appropriate column nameNULL
value treatment varies by vendor- Can use column numbers rather than column names when using
ORDER BY
- However, the columns to be ordered must then be included in the
SELECT
clause - For obvious reasons, this is considered quite a code smell
- However, the columns to be ordered must then be included in the
ORDER BY
clause is often used in conjunction withGROUP BY
clause- Tables, cursors, & relations
- In math a set has no order
- SQL tables are designed as relations - i.e., mathematical sets - ergo unordered
- Using a
SELECT
statement without anORDER BY
clause- db engine will give you your results in any way it chooses
- i.e., the resulting order of rows will be non-deterministic
- When you do use an
ORDER BY
clause- The result is then not a set, ergo not a relation, ergo not a table
- With SQL, the result is called a cursor
- Significance of working with a table vs a cursor
- Some SQL language elements & operations assume they are working on a table
- Examples:
- Table expressions
- Set operators
- The
SELECT
clause &ORDER BY
clause- Omitting column(s) in the
ORDER BY
clause from theSELECT
clause- Perfectly legal SQL code
- However, you then cannot proof-read your results
- Including
DISTINCT
in yourSELECT
clauseORDER BY
clause can then only use columns included in theSELECT
clause- Any column not the
SELECT
clause could well have a range of values for each displayed row ORDER BY
can of course not sort rows if a column on which it is to sort could hold multiple values for a given row in the resulting cursor
- Omitting column(s) in the
TOP
&OFFSET-FETCH
filtersTOP
[T-SQL proprietary]- Used within the
SELECT
clause - Depends on the
ORDER BY
- However,
ORDER BY
clause is processed after theSELECT
- With the
TOP
filter T-SQL is then getting a dual use out ofORDER BY
- However,
- Can specify either the number or a percentage of rows to return
- Note:
TOP
rounds up the number of rows returned ifPERCENT
yields other than a whole number - When your
SELECT
clause usesDISTINCT
keywordTOP
applies to the distinct rows - Ties in ranking
- Can make your results non-determinsitic (i.e., more than one result would be considered correct)
- In fact, to get really random results it is in fact permissible to use the
TOP
filter sans anORDER BY
clause - The
WITH TIES
option- Results will be deterministic
- You may then well get more than the number of rows requested
- Table or cursor?
- That
ORDER BY
now does 'double duty'- Used to control the order by which data are presented
- Also used to control how
TOP
filtering works
- The use of
ORDER BY
typically indicates that the result will be a cursor- However, it is possible to employ
TOP
yet still generate (ultimately) a table - It is also possible to order/rank data on one column for the
TOP
filtering, while displaying data by applyingORDER BY
to a different column - To do either of these scenarious requires the use of table expressions (discussed later)
- However, it is possible to employ
- That
- Syntax
- Example (from Ben-Gan: 3rd Ed., pp. 44‑5):
SELECT TOP (5) [PERCENT] orderid, orderdate, custid, emid
FROM Sales.Orders
ORDER BY orderdate DESC - Example (from Ben-Gan: 3rd Ed., pp. 44‑5):
-- Including WITH TIES
SELECT TOP (5) WITH TIES orderid, orderdate, custid, emid
FROM Sales.Orders
ORDER BY orderdate DESC - Example (from Ben-Gan: 3rd Ed., pp. 44‑5):
-- To ensure deterministic order include a column with unique values in ORDER BY SELECT TOP (5) orderid, orderdate, custid, emid
FROM Sales.Orders
ORDER BY orderdate DESC, orderid DESC;
- Example (from Ben-Gan: 3rd Ed., pp. 44‑5):
- Used within the
OFFSET-FETCH
- Part of U-SQL
- Considered an extension of the
ORDER BY
clause - Can only use
OFFSET-FETCH
when you have anORDER BY
clause OFFSET
indicates the number of rows to skipFETCH
indicates the number of rows to return- Syntax:
Offset_Fetch :=
['OFFSET' integer_or_long_literal ('ROW' ¦ 'ROWS')] [Fetch].
Fetch :=
'FETCH' ['FIRST' ¦ 'NEXT'] integer_or_long_literal ['ROW' ¦ 'ROWS']
['ONLY']. - Notes:
- Skipping
OFFSET x ROWS
- U-SQL allows this, & is equivatent to
OFFSET 0 ROWS
- T-SQL requires that an
OFFSET
clause wheneverFETCH
is employed
- U-SQL allows this, & is equivatent to
- Skipping the
FETCH
clause gives you all rows after the offset - Keyword flexibility
- The
ROW
&ROWS
keywords are interchangeable FIRST
&NEXT
are also interchangeable
- The
- Skipping
- Example (from Ben-Gan: 3rd Ed., p. 47):
SELECT orderid, orderdate, custid, empid
FROM Sales.Order
ORDER BY orderdate, orderid
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;
- Window functions (briefly)
- Defined
- A function that operates operates on some subset of your underlying query for that querey
- Returns a scalar result
- SQL standard (T-SQL supports a subset of window functions)
- Placed within the
SELECT
clause - Syntax
- Begin with a window function, e.g.,
ROW_NUMBER()
- Follow with
OVER()
- Inside the
OVER()
clause- This is what returns is the subset of rows on which the window function operates
- Specifically,
PARTITION BY
clause identifies the column thatOVER()
filters on for the subquery - Can then include an
ORDER BY
clause
- After the
OVER()
clause a thoughtful data specialist supplies a column alias
- Begin with a window function, e.g.,
- Example (from Ben-Gan: 3rd Ed., p. 48):
SELECT orderid, custid, val,
ROW_NUMBER() OVER(PARTITION BY custid
ORDER BY val) AS rownum
FROM Sales.OrderValue
ORDER BY custid, val; - This statement will:
- Return all orders, with their
orderid
&value
- Sort the rows first by
custid
, & then within eachcustid
byval
- Assign a unique row number, which resets to 1 for each new
custid
(courtesy of thePARTITION BY
clause)
- Return all orders, with their
- Defined
- General
- General
- Beyond the
SELECT
clause- Predicates & operators
- Non-standard operators
- T-SQL recognizes the following non-standard comparison operators:
!=, !>, !<
- Ben-Gan (3rd Ed., p. 50) recommends avoiding them
- T-SQL recognizes the following non-standard comparison operators:
- Precedence
- Just as multiplication has precendence over addition in arithmatic (e.g., 2 * 5 + 7 = 17), SQL operators have an order of precedence
- From highest to lowest:
()
*, /, %
+, -
(including+
for concatenation)=, >, <
, etc. (i.e., the comparison operators)NOT
AND
BETWEEN, IN, LIKE, OR
=
(when used as assignment)
- Data types of scalar expressions
- If data types of operands differ the result will be of the data type with the higher precedence
- If operands of of the same data type result will be of the same type
- Examples:
- 5 / 2 will yield 2
- 5 / 2.0 will yield 2.5
- Casting example:
-- Assume col1 and col2 are INT columns
-- Assume you want to ensure quotient is NUMERIC
CAST(col1 AS NUMERIC(12,2) / CAST(col2 AS NUMERIC(12,2)
- Non-standard operators
CASE
expressions- Part of ANSI SQL
- An expression, not a statement - i.e, it doesn't take an action
- Can be used wherever scalar expressions are allowed
- Note: if you omit an
ELSE
clause SQL imputesELSE NULL
- Simple form
- Compares a single value to a list of possible values
- Syntax/example:
SELECT breakfastid, breadfastname, categoryid,
CASE categoryid
WHEN 1 THEN 'Spam & Ham'
WHEN 2 THEN 'Spam & Egg'
WHEN 3 THEN 'Spam, Ham, & Egg'
WHEN 4 THEN 'Spam, Spam, Spam, & Ham'
WHEN 5 THEN 'Spam, Spam, Spam, & Spam'
ELSE 'Plain Spam'
END AS categoryname
FROM Menu.Breakfast
- Searched form
- You use predicates (i.e., comparisons) rather than equality in the
WHEN
clauses - Place the value you are testing inside the
WHEN
clause - Syntax/example:
SELECT breakfastid, breadfastname, price,
CASE
WHEN price < 5.00 THEN 'Ugh. Small Time Customer'
WHEN price BETWEEN 5.00 AND 15.00 THEN 'Ok, pay attention'
WHEN price > 15.0 THEN 'Offer the VIP Lounge'
ELSE 'Missing price'
END AS waitressinstruction
FROM Menu.Breakfast;
- You use predicates (i.e., comparisons) rather than equality in the
- Note: You can always convert a simple
CASE
expression to a searched one (though not visa versa) - 'Equivalent' functions
- Certain functions are effectively abbreviated
CASE
expressions COALESCE()
- standard- Non-standard
ISNULL()
- T-SQL-specificIIF()
- added to facilitate migration from MS Access dbsCHOOSE()
- added to facilitate migration from MS Access dbs
- Certain functions are effectively abbreviated
NULLs
- SQL supports 3-value predicate logic:
TRUE
FALSE
UNKNOWN
- Logical expressions
- When there are no
NULLs
involved will always give you eitherTRUE
orFALSE
- However, whenever there is a missing value - i.e., a
NULL
- the expression will evaluate toUNKNOWN
UNIQUE
column constraintNULLs
in the column are all considered "equal"- Ergo, the column can contain one
NULL
value, but that is all
- When there are no
- Treatment of an
UNKNOW
resultWHERE
&HAVING
clauses- These only accept expressions which evaluate to
TRUE
- Ergo, expressions evaluating to
UNKNOWN
are filtered out
- These only accept expressions which evaluate to
CHECK
(i.e., column & table) constraints- These equate to "reject
FALSE
" - Ergo, expressions evaluating to
UNKNOWN
are accepted
- These equate to "reject
- Negating
UNKOWN
- The value remains
UNKNOWN
- Example (assume an HR record contains
NULL
in a "salary" field):- The record will be omitted for a
WHERE salary > 0
clause - Perhaps counter-intuitively, the record will be also be omitted for a
WHERE NOT (salary > 0)
clause - Note: To get what you probably want with 2nd clause re-write it as:
WHERE salary = 0 OR salary IS NULL
- The record will be omitted for a
- The value remains
GROUP BY
&ORDER BY
NULLs
will all appear togetherNULLs
will appear before present values
- SQL supports 3-value predicate logic:
- All-at-once operations
- "…all expressions that appear in the same logical query processing phase are evaluated logically at the same point in time." (Ben-Gan: 3rd Ed., p. 58)
- Implication for
SELECT
clauses- All elements of clause are evaluated simultaneously
- Ergo, you cannot create an alias for one column & then refer to that alias elsewhere within the clause
- Example:
-- This is invalid…
SELECT orderid, YEAR(oderdate) AS orderyear, orderyear + 1 AS next year
FROM Orders;
- Implication for other clauses
- Suppose you are dividing one column by another, but you want to guard against division by zero
- Example:
-- This is problematic…
SELECT col1, col2
FROM mytable
-- No guarantee that 1st part of WHERE clause evaluated b4 2nd
WHERE col2 <> 0 AND col1/col2 > 10; - Solution (because CASE statements ARE evaluated sequentially):
-- omitting SELECT & FROM
WHERE
CASE
WHEN col2 = 0 THEN 'no'
WHEN col1 / col2 > 10 THEN 'yes'
ELSE 'no'
END = 'yes;'
- Working with character data
- Operators & functions
- Concatenation
- Can use:
+
signCONCAT()
function
- If working with national characters, even spaces need
N
prefix - Example:
SELECT firstname + N' ' + lastname AS fullname
NULLs
- Reminder: any calculation - even concatination - which includes a
NULL
operand will returnNULL
- Use
COALESCE
to replace aNULL
string with an empty string - i.e.,N' '
- Also,
CONCAT()
is nice because it assumes that you always want to replaceNULL
with an empty string
- Reminder: any calculation - even concatination - which includes a
- Can use:
SUBSTRING(string, start, length)
- Extracts a substring from a string
- Note: any function 'like' this treats 1st character in a string as position #1
LEFT(string, n) & RIGHT(string, n)
LEN(string)
- Counts number of characters in a string
- Ignores blanks
- DATALENGTH(string) - 'same' as
LEN
except counts:- Bytes rather than characters (will differ when using Unicode)
- Includes blank spaces in the count
CHARINDEX(substring, string [, start_pos])
- finds 1st occurrence of a character in a stringPATINDEX(pattern, string)
- finds 1st occurrence of a pattern in a stringREPLACE(string, substring1, substring2)
REPLICATE(string, n)
- repeats a stringn
timesUPPER(string) & LOWER(string)
STUFF(string, pos, delete_length, insert_string)
- swap one substring for anotherRTRIM(string) & LTRIM(string)
- There is no bi-lateral
TRIM()
function - Instead, do
RTRIM(LTRIM(' emsp;Hello, World emsp;'))
- There is no bi-lateral
FORMAT(string, format_string, culture)
- There are numerous standard formats (see https://docs.microsoft.com for details)
- Can also create custom formats
- Ben-Gan claims function is generally "more expensive" than alternatives (3rd Ed., p. 69)
COMPRESS(string)
COMPRESS
turns string (or character) data into binary- Compressed data needs to be stored in a column typed for
VARBINARY(MAX)
DECOMPRESS(expression)
- Like
COMPRESS
will also return aVARBINARY(MAX)
type (i.e., a byte array) - You normally then
CAST
that binary array - Example:
SELECT
CAST(
DECOMPRESS(COMPRESS('some long text…'))
AS NVARCHAR(MAX)) AS somecolumnname;
- Like
STRING_SPLIT(string, separator)
- All other string functions return a scalar value
STRING_SPLIT
, however, returns a table- Will have a single column called
val
- There will be one record for each 'split' substring
- Will have a single column called
- When working with a string of number-as-text values you can cast the result
- Example:
SELECT CAST(value as INT) AS myints
FROM STRING_SPLIT('98,97,96,95', ',');
- Concatenation
- The
LIKE
predicate%
(percent) wildcard- Represents a string of any size - including the empty string
- Example:
WHERE lastname LIKE N'D%'
gives you last names Doe, Deer, etc. - Note:
- Can often get same result using
SUBSTRING() & LEFT()
functions - However, especially when your pattern starts with a known prefix
LIKE
tends to work more efficiently
- Can often get same result using
_
(underscore) wildcard - stands in for a single character[<list of characters>]
wildcard- Filters for a single character listed within the brackets
- Example:
WHERE lastname LIKE N'[ABC]'
gives you all last names beginning with A, B, or C
<character> - <character>
wildcard- Use to specify a range of characters
- Example:
WHERE lastname LIKE N'[A-M]%'
gives you last names in 1st half of alphabet
[∧<character list or range>]
wildcard- Use to specify a range of characters which should be excluded
- Example:
WHERE lastname LIKE N'[∧A-M]%'
gives you last names in 2nd half of alphabet
ESCAPE
character/word- Use this when you need to search for wildcard characters themselves - e.g.,
%, _, [, or ]
- Add the word
ESCAPE
at the end of your expression, then indicate the escape character you have chosen - Example:
compname LIKE '%!_%' ESCAPE '!'
to get all company names which include an underscore - Notes:
- When chosing your excape character make sure that it won't itself show in in your searched data
- For
%, _, [
wildcards you can use square brackets as your escape 'character' - Example:
compname LIKE '%[_]%'
to get all company names which include an underscore
- Use this when you need to search for wildcard characters themselves - e.g.,
- Operators & functions
- Working with date & time data
- Date & time types
DATETIME
- Includes: both date & time elements
- Storage: 8 bytes
- Date range: Jan. 1, 1753 - Dec. 31, 9999
- Accuracy: ≈3 milliseconds
- Recommended entry format:
'YYYYMMDD hh:mm:ss.nnn'
SMALLDATETIME
- Includes: both date & time elements
- Storage: 4 bytes
- Date range: Jan. 1, 1900 - June 6, 2079
- Accuracy: ≈1 minute
- Recommended entry format:
'YYYYMMDD hh:mm'
DATE
- Includes: date only
- Storage: 3 bytes
- Date range: Jan. 1, 0001 - Dec. 31, 9999
- Accuracy: ≈1 day
- Recommended entry format:
'YYYY-MM-DD'
TIME
- Includes: time only
- Storage: 3 - 5 bytes
- Date range: N/A
- Accuracy: ≈100 nanoseconds
- Recommended entry format:
'hh:mm:ss:nnnnnnn'
(out to 7 decimal places)
DATETIME2
- Includes: both date & time elements
- Storage: 6 - 8 bytes
- Date range: Jan. 1, 0001 - Dec. 31, 9999
- Accuracy: ≈100 nanoseconds
- Recommended entry format:
'YYYY-MM-DD hh:mm:ss:nnnnnnn'
(out to 7 decimal places)
DATETIMEOFFSET
- Includes: date only
- Storage: 8 - 10 bytes
- Date range: Jan. 1, 0001 - Dec. 31, 9999
- Accuracy: ≈100 nanoseconds
- Recommended entry format:
'YYYY-MM-DD hh:mm:ss:nnnnnnn [+¦-] hh:mm'
(out to 7 decimal places)
- Notes re storage size:
- For
TIME, DATETIME2, & DATETIMEOFFSET
depends on the precision you specify - Precision specified as a fraction of a second, out to 7 places
- Example:
DATETIME2(3)
gives you one millisecond precision - Specifying a precision of 0 means 1-second
- If precision is not specified SQL Server defaults to precision of 7
- For
- Literals
- Formatted correctly, SQL Server will convert character-string literals to date/time
- Again, SQL Server converts lower-precedent types to higher-precedent types
- This means you do not have to employ the
CAST()
orCONVERT()
functions - Language dependence
- In Europe, for example, numerical dates are written
dd/mm/yyyy
- With SQL Server, you control how date literals are interpreted by playing with:
SET LANGUAGE
commandDATEFORMAT
settings
- Note: these settings control how SQL Server reads date literals, not how it prints dates
- In Europe, for example, numerical dates are written
- Recommended approach, however:
- Always employ one of these 2 formats:
-
'YYYYMMDD hh:mm:ss.nnn'
-
'YYYY-MM-DDThh:mm:ss.nnn'
-
- These formats are language neutral
- Always employ one of these 2 formats:
- Note: for data types including a time component SQL Server assumes midnight if you do not specify a time
- Working with date & time separately
- If you are working just with dates or just with times:
- You should stick to
DATE & TIME
data types - If you cannot work with those 2 data types (typically because of legacy concerns) use
- Midnight for the time with date types
- January 1, 1900 as the date component with time entries
- You should stick to
- Note:
- You are not required to specify a time element when working with, say, a
DATETIME
column - If you write, say,
'20120405'
SQL Server will assume/add midnight as time component
- You are not required to specify a time element when working with, say, a
- Check constraint
- You can add a table constraint that ensures only midnight is ever used as a time component
- Syntax/example (from Ben-Gan: 3rd Ed., pp. 78‑9):
ALTER TABLE Sales.Orders2
ADD CONSTRAINT CHK_Orders2_orderdate
CHECK(CONVERT(CHAR(12), orderdate, 114) = '00:00:00:000'); - For a column where you only want time you can also create a table constraint (though time-only entries would be a little weird)
- If you are working just with dates or just with times:
- Filtering date ranges
- In general, you do not want to employ functions like
YEAR() & MONTH()
inWHERE
clauses - This is because "in most cases,when you apply manipulation on the filtered column, SQL Server cannot use an index in an efficient manner." (Ben-Gan: 3rd Ed., p. 80)
- Recommended syntax:
-- To retrieve 1Q 2015 orders.
SELECT orderid, custid, empid, orderdate
FROM Sales.Orders
WHERE orderdate >= '20150101' AND orderdate < '20150401';
- In general, you do not want to employ functions like
- Date & time functions
- Current data & time
- Functions
GETDATE()
- non-standardCURRENT_TIMESTAMP
- No parentheses used
- Standard
- Because it gives exact same result as
GETDATE()
& is standard, prefer this
GETUTCDATE()
SYSDATETIME()
SYSUTCDATETIME()
SYSDATETIMEOFFSET()
- Notes:
- You will save yourself many, many tears if you always record events in UTC time
- If you must work with, say, date only:
CAST(SYSUTCDATETIME() AS DATE)
- Functions
CAST(), CONVERT(), & PARSE()
- & theirTRY_
counterparts- The 'plain' versions of these functions will blow up your query if unable to convert data type successfully
- The
TRY_
versions returnNULL
if they cannot covert the data as specified - All functions require at least the value to be converted & the target data type as parameters
- The
CONVERT()
functions take an optionalstyle_number
parameter - The
PARSE()
functions take an optionalUSING culture
parameter - Note:
CONVERT() & PARSE()
are non-standardCAST()
is standard, so, ceteris parabis, preferCAST()
SWITCHOFFSET()
- Manipulates
DATETIMEOFFSET
value (i.e., a variable which specifies the offset from UTC) - Syntax/example:
-- To convert sysdate to GMT.
SELECT SWITCHTIMEOFFSET(GETUTCDATE(), '+00:00');
- Manipulates
TODATETIMEOFFSET()
- Takes as an input a date & time value which does not have an embedded UTC offset
- Typically use this for entries made in local time that you want to make UTC times
AT TIME ZONE
- Takes as an input a date & time value which does not have an embedded UTC offset
- Then employs a text representation of the time zone to which you want to convert
- Note: automatically handles daylight savings time
- Syntax/example:
-- Converting noon Eastern in February.
SELECT
CAST('20210215 12:00:00.0 -5:00' AS datetimeoffset)
AT TIME ZONE 'Pacific Standard Time' AS val1;
-- Gives you 9:00 AM -08:00
-- If converting in August UTC offset is -4:00!
SELECT
CAST('20210815 12:00:00.0 -4:00' AS datetimeoffset)
AT TIME ZONE 'Pacific Standard Time' AS val2;
-- Correctly gives you 9:00 AM -07:00 [NOT -08:00]
DATEADD()
- Adds a specified number of dayparts to a datetime value
- Among the valid dayparts, not entered as text:
year
quarter
month
dayofyear
hour, minute, second
- Others
- Return type will be the same as the input type
- String literal inputs will return a
DATETIME
value
DATEDIFF() & DATEDIFF_BIG()
- Gives you the difference between 2 date/time values in terms of a specified date part
- Both return an integer
DATEDIFF()
returns anINT
(4 bytes)DATEDIFF_BIG()
returns aBIGINT
(8 bytes)
- Various ways to combine
DATEADD()
&DATEDIFF()
to get beginning/end-of dates for months & years (see Ben-Gan: 3rd Ed., pp. 86‑7)
DATEPART()
- Returns an
INT
for specified date & time value - As with
DATEADD()
you specify the daypart not entered as text - Syntax/example:
-- Will return 10
SELECT DAYPART(month, '20221006')
- Returns an
YEAR(), MONTH(), & DAY()
- Pass in a datetime (or appropriate string)
- Return integers
DATENAME()
- Like
DAYPART()
- However, returns the appropriate value as a string (e.g., 'October') rather than as an integer (e.g., 10)
- Like
ISDATE()
- Pass in a string
- Returns 1 if convertible to a datetime value, otherwise returns 0
FROMPARTS()
functions- All take integers as parameters
- Examples:
DATEFROMPARTS(year, month, day)
DATETIME2FROMPARTS(year, month, day, hour, minute, seconds, factions, precision)
- Others
EOMONTH()
- Returns a
DATE
value from given input - Can optionally add a
months_to_add
argument
- Returns a
- Current data & time
- Date & time types
- Querying metadata
- For Catalog views see learn.microsoft.com View documentation ¦ SQL Server ¦ Development/Transact-SQL (T-SQL) ¦ System Catalog Views
- For Information schema views see learn.microsoft.com View documentation ¦ SQL Server ¦ Development/Transact-SQL (T-SQL) ¦ System Information Schema Views
- For System stored procedures see learn.microsoft.com View documentation ¦ SQL Server ¦ Development/Transact-SQL (T-SQL) ¦ System stored procedures
- For System functions see learn.microsoft.com View documentation ¦ SQL Server ¦ Development/Transact-SQL (T-SQL) ¦ System functions
- Combining the results of multiple queries
- General
- Syntax: the
UNION, INTERSECT,
&EXCEPT
statements all simply go between completeSELECT
statements - Logic is based on set theory
- Syntax: the
UNION
- The individual queries must return an equal number of expressions/columns
- Further, data types must be compatible
- An
ORDER BY
clause only effects the combined results; it cannot work on the component queries - Default is to exclude duplicate records
- Use
UNION ALL
to override that behavior - Note:
UNION ALL
violates set theory
INTERSECT
- Can get same results using a
WHERE
clause with anIN
subquery - Can also get same results using a
WHERE EXISTS
subquery
- Can get same results using a
EXCEPT (MINUS)
- Can get same results using a correlated subquery
- A correlated subquery is one that accepts a parameter from an outer query as a criterion in the
WHERE
clause
- General
- Predicates & operators
- The
- Multi-Table & Advanced Queries
- Joins
- General
- The result of joins is a virtual table
- Table aliases
- You really should always use them with joins, Dahling
- If not used column names in your result will be qualified by table name - e.g., Employee.employeeid
- Cross joins (Cartesian product)
- General
- Returns all possible pairs of rows
- Cross joins are usually quite costly in terms of processing
- Cross joins are also almost inevitably a mistake in SQL coding
- SQL:1992/2003 standard syntax (sample):
SELECT c.name, p.number
FROM customer c
CROSS JOIN phone p - Old, 1989 syntax (sample):
SELECT c.name, p.number
FROM customer c, phone p - Note: If you are writing joins in the old syntax & forget your
WHERE
clause you wind up with a cross join! - Self cross joins
- Aliasing table names is required
- Syntax/example from (Ben-Gan: 3rd Ed., p. 105):
SELECT
E1.empid, E1.firstname, E2.lastname,
E2.empid, E2.firstname, E2.lastname
FROM HR.Employees AS E1
CROSS JOIN HR.Employees AS E2
- Producing tables of numbers
- Pretty cool technique (see Ben-Gan: 3rd Ed., pp. 106‑7)
- Create a table of 10 digits
- Then run multiple cross joins, multiply then adding results
- Syntax/example:
DROP TABLE IF EXISTS dbo.MyNumbers
CREATE TABLE dbo.MyNumbers
(num int)
INSERT INTO MyNumbers
SELECT d1.digit + d2.digit*10 + d3.digit*100 as num
FROM dbo.TenDigits as d1
CROSS JOIN dbo.TenDigits as d2
CROSS JOIN dbo.TenDigits as d3
ORDER BY num
- General
- Inner joins
- General
- Inner joins return only rows that match between two tables - i.e., on the join condition
- All other rows are excluded
- Algebraic logic
- Start with a the cartesian product of the 2 tables
- Then filter out rows using join condition
- Two syntaxes for inner joins
- SQL:1992/2003 syntax:
-- Note that INNER keyword is optional
SELECT …
FROM <table1>
[INNER] JOIN
<table2>
[ON <condition>] ¦ [USING <column_name>,…],… - SQL:1989 syntax:
SELECT …
FROM <table1>
AS <alias1>, <table2> AS <alias2>
WHERE alias1.columnx = alias2.columny - Inner join safety
- If you forget the WHERE clause with the '89 syntax
- You get a (often huge) cross join
- You also may not notice the error, because it is valid syntax
- In a large, complicated query the error is suprisingly easy to make
- If you forget the ON clause with the '92 syntax you simply get an error message
- If you forget the WHERE clause with the '89 syntax
- SQL:1992/2003 syntax:
- Composite joins
- This is where you have multiple join conditions
- Tend to be encounter where you have composite primary/foreign keys
- Syntax/example:
FROM dob.Table1 AS T1
INNER JOIN dbo.Table2 AS T2
ON T1.col1 = T2.col1
AND T1.col2 = T2.col2
- Nonequijoin
- Generally used for cases where you are not joining on primary key/foreign key relationships
- Can certainly show up in composite joins
- Syntax/example (from Ben-Gan: 3rd Ed., pp. 111‑2)
-- To generate unique pairs of employees
SELECT
E1.empid, E1.firstname, E1.lastname,
E2.empid, E2.firstname, E2.lastname
FROM HR.Employees AS E1
INNER JOIN HR.Employees AS E2
ON E1.empid < E2.empid;
- Joining more than two tables
- General
- Technically you can only join 2 tables at once
- However, the result of a join is a (single) virtual table
- Ergo, string your joins together to achieve a multi-table join
- Number of joins
- You need at least (n-1) joins in an n-table query
- Where you can wind up with more than (n-1) joins
- You are running a nonequijoin
- You have composite primary/foreign keys
- Notes: if you fail to employ at least (n-1) joins in an n-table query:
- You will wind up with a Cartesian product
- You don't have to forget the
WHERE
clause for this little disaster - When you are joining multiple tables the number of rows produced by a Cartesian product can become mind-numbing
- SQL:2003 Syntax/example:
SELECT c.cust_name,
oh.ordhdr_no,
s.status_descr
FROM dbo.customer AS c
JOIN
dbo.order_header AS oh
ON c.id = oh.custid
JOIN
dbo.status AS s
ON s.id = oh.statusid - Old Syntax/example:
SELECT c.cust_name,
oh.ordhdr_no,
s.status_descr
FROM dbo.customer AS c,
dbo.order_header AS oh,
dbo.status AS s
WHERE c.id = oh.custid
AND s.id = oh.statusid
- General
- General
- Outer joins: joining tables on columns containing
NULL
values- Basic
- General
- Introduced in SQL '92
- As such, there is only one syntax
- Logical steps
- Start with a Cartesean product (i.e., a cross join)
- Filter out those records which do not meet the
JOIN
condition (as with an inner join) - Add back all excluded records from one of the tables
- Called adding outer rows
- That table is called the preserved table
- Join types
LEFT OUTER JOIN
RIGHT OUTER JOIN
FULL OUTER JOIN
- Preserved table row types (this might be unique to Ben-Gan (see 3rd Ed., p. 115)
- Inner rows - those which have matches in the 2nd table based on the
ON
condition - Outer rows - those which do not have such matches
- Inner rows - those which have matches in the 2nd table based on the
ON
vs.WHERE
clause- Which clause to use when adding a predicate (filter) can be confusing'
ON
clause- This is not the final filter applied to the preserved side
- Only determines matching, not whether a row is included in final output
WHERE
clause- Processed after the
FROM
clause - Ergo, is processed after all joining is conducted
- Controls whether a row is included in final output
- Processed after the
- To return only outer rows
- Very common scenario
- Screen for
NULL
values in the non-preserved table, in one of the following:- A column used in the
JOIN
statement - A primary key column
- A column defined as
NOT NULL
- A column used in the
- Example: you want to see customers who have placed no orders
- Syntax/example (from Ben-Gan: 3rd Ed., p. 116):
SELECT C.custid, C.companyname
FROM Sales.Customers AS C
LEFT JOIN Sales.Orders AS O
ON O.custid = C.custid
WHERE O.custid IS NULL; - When screening for
NULL
values- Be careful only to use
IS NULL
- Using
= NULL
always returnsUNKNOWN
, &WHERE
clauses only accept values ofTRUE
- Be careful only to use
- General
- Advanced outer joins
- Including missing values
- Filtering attributes from the non-preserved side of an outer join
- Using outer joins in a multi-join query
- Using
COUNT
with outer joins NULLs
are used as placeholders on the "joined" column(s) of the non-preserved rows- Two syntaxes for outer joins
- SQL:2003 standard syntax
- Left outer join
- SQL:2003 standard syntax
- Right outer join
- SQL:2003 standard syntax
- Old syntax
- Full outer join
- Union join
- Basic
- Joins involving inline views
- Multitable joins with correlated queries
- Improving the efficiency of multitable joins
- General
- Joins
- SQL Functions
- General
- Numeric functions
CEIL
ROUND
TRUNC
RAND
SIGN
- String functions (see Working with character data)
- Date & time functions (see Working with date & time data)
- Aggregate functions
- General
SUM
COUNT
AVG
MIN
&MAX
- Conversion functions
- General
- Conversion between different data types
- Conversion between different character sets
- Data type-specific conversion functions
- System functions
- Miscellaneous functions
DECODE
&CASE
COALESCE
&NULLIF
NVL, NVL2
, &ISNULL
- User-defined functions
- SQL Operators
- General
- Arithmetic & String Concatenation Operators
- Logical Operators
ALL
ANY ¦ SOME
BETWEEN Inexpression> AND <expression>
IN
EXISTS
LIKE
AND
NOT
OR
- Operator Precedence
- Assignment Operator
- Comparison Operators
- Bitwise Operators
- Single-table queries
- Security & the System Catalog
- RDBMS Security
- The System Catalog & INFORMATION_SCHEMA
- Procedural Programming & Database Access Mechanisms
- Stored Procedures, Triggers, & User-Defined Functions
- SQL & XML
- SQL & Procedural Programming
- Bibliography
- Ben-Gan, Itzik. SQL Pocket Guide USA: Microsoft Press, 2009. Print.
- Gennick, Jonathan. Microsoft SQL Server 2008 T-SQL Fundamentals. 3rd Ed. Canada: O'Reilly, 2011. Print.
- Kriegel, Alex and Boris M. Trukhnov. SQL: Second Edition. Indianapolis, IN: Wiley Publishing, Inc., 2008. Print.
- Ben-Gan, Itzik. COALESCE vs. ISNULL ITPro Today (formerly SQL Server Pro) April 16, 2013.
- SQL Basic Concepts
- Access 2010
- .NET
- VSTO 3.0
- VSTO Overview
- Office Programming
- Office business applications (OBAs)
- VSTO 3.0 = Visual Studio 2008 Tools for Office 2007
- Office Primary Interop Assemblies (PIAs)
- VSTO 3.0 supports both Office 2007 & Office 2003
- Office Object Models
- Objects (e.g., Application, Workbook, Document)
Application
is always the root object- Child objects exist as properties of the parent object
- Most Office objects are not new-able
- Syntax/example (declaring a series of objects as you drill down to a worksheet):
// Assume root XL application object passed to code as "app"…
Excel.Workbooks myWbks = app.Workbooks;
Excel.Workbook myWbk = myWbks.get_Item(1);
Excel.Sheets myWshs = myWbk.Worksheets;
Excel.Worksheet myWsh = myWshs.get_Item(1) as Excel.Worksheet; - Syntax/example (if you do NOT need to create/hold on to parent objects):
// Assume root XL application object passed to code as "app"…
Excel.Worksheet myWsh2 =
app.Workbooks.get_Item(1).Worksheets.get_Item(1) as Excel.Worksheet;
- Collections
- Collections in Office object models are almost always 1-based
- With
item
property parameter is of typeobject
- Can therefore pass in either a string (i.e., object name) or an integer
- .NET will box the string or integer as an object
- Note: See discussion below on Parameterized Properties - C# vs. VB
- Items within VSTO Office collections are themselves of type
object
- This is unlike VBA, where, for example, members of Excel's
Workbooks
collection areWorkbook(s)
- Therefore, you typically need the cast the object returned from a collection
- Example:
Dim myWb as Excel.Workbooks = _
app.Workbooks.Item("SomeName.xlsx")
Dim ws as Excel.Worksheet
ws = Ctype(myWb.Worksheets("SomeName"), Excel.Worksheet)
- This is unlike VBA, where, for example, members of Excel's
- Deleting objects from an Office collection
- Deleting directly from such a collection can lead to tears
- Best Practice:
- Create a .NET collection (e.g., list or an array) to hold items to be deleted
- Iterate through the Office collection, & as to-be-deleted objects are found add them to a custom .NET collection
- Iterate through the .NET collection & invoke each object's
Delete()
method as you go
- Possibilities when requesting an object from an empty collection
- Run-time error
null
value- (See Office documentation for specifics on each collection)
- Enumerations
- In C# you must qualify the enumeration value with its type
- Example (Word):
wdWindowsStateNormal
will not compile - must useWdWindowsState.wdWindowsStateNormal
- Objects (e.g., Application, Workbook, Document)
- Properties, Methods, & Events
- Properties
- General
- Can return other Office object model objects
- Example: In Word
ActiveDocument
is a property of the WordApplication
object - Programming pitfall: asking for an Office object which is either non-existent or nonsensical
- Example: Calling on the
ActiveDocument
property of the WordApplication
in instances where Word has no open documents - (See discussion above on Possibilities when requesting an object from an empty collection)
- Example: Calling on the
- Parameterized Properties: C# vs. VB
- C# does not accept parameterized properties
- Therefore, most properties in the Office object model that take parameters are converted to methods for C#
- The replacement methods have a
get_
orset_
prefix - Example:
Excel.Workbook myWb = app.Workbooks.get_Item(1);
- In VB this can be written as:
Dim myWb as Excel.Workbook = app.workbooks.Item(1)
- The replacement methods have a
- Some Office object model objects, however, are extended by VSTO
- With these can access parameterized property via an indexer
- Syntax: same as an array
- Use square brackets
- Example:
Range[parameter1, parameter2]
, instead ofget_Range(parameter1, parameter2)
- Optional parameters
- VB: you can simply omit optional parameters
- C#: does not allow parameters to be omitted
- Generally optional parameters are of type
object
- Must declare the parameter using
System.Type.Missing
- In some instances an optional parameter is not of type
object
but is some enumerated value - In these cases must look up & declare the default enumeration
- Generally optional parameters are of type
- Optional parameters in Word
- Word requires that optional parameters be passed by reference
- Therefore in C# cannot pass in
System.Type.Missing
- Therefore, must declare a variable, set it to
System.Type.Missing
, & then pass in a reference to that variable (which one usually calls "missing
,")
- Common properties
- Because you are working in .NET can course get
GetType(), GetHashCode(), Equals()
, &ToString()
method on every Office object in VSTO - Conversely, because you are working with Office objects frequently find objects with the following properties
Application
- returns root Application objectCreator
- the application in which the object was createdParent
- Because you are working in .NET can course get
- General
- Methods
- Optional parameters
- Can be omitted in VB (just as they can be with properties)
- Pre-.NET 4.0 must declare the parameter using
System.Type.Missing
in C#
- Optional parameters in Word
- In VB
- Optional parameters may simply be omitted
- Can also use named parameters
- In C# optional parameters must be passed by reference
- Explanation:
- In Word's underlying COM programming optional parameters are specified as
pointers
toVARIANTS
- [Note: For whatever reason, Excel typically does not use a pointer to a
VARIANT
when specifying optional parameters.] - Office interops translates
pointers
–to–VARIANTS
in COM into references–to–objects in .NET - Lastly, the way that the Office/Word interop is written, & due to the nature of COM assemblies, it is simply impossible for Word properties & methods to redefine (i.e., mistakenly) the referenced object as anything other than
System.Type.Missing
- In Word's underlying COM programming optional parameters are specified as
- In VB
- Optional parameters
- Events
- General
- Similar events can be raised by multiple objects
- Example:When a worksheet is activated in Excel it raises events on the
Worksheets
collection & on theApplication
object - Be careful of the object in which you place event-handling code
- Note: Signature of an event will of course differ slightly based on which object raises it
- VB event-handling (standard)
- Instantiate an object using the
WithEvents
keyword - Use
Handles
keyword
- Instantiate an object using the
- C# & VB-alternate event-handling (
AddHandler
method)- Declare a handler method which matches the signature of the even being raised
- Attach handling method to the object raising the event
- Can, of course, also provide methods to detach event-handler
- VB Example: the Open event on Excel application object
- Delegate signature:
public delegate AppEvents_WorkbookOpenEventHandler(ByVal Wb As Workbook)
- Variable declaration:
Dim xlApp as Excel.Application
Dim EventDel_BeforeBookOpen as _
Excel.AppEvents_WorkbookBeforeOpenEventHandler - Connect handler:
xlApp = CreateObject("Excel.Application")
EventDel_BeforeBookOpen = New _
Excel.AppEvents_WorkbookBeforeOpenEventHandler( _
SomeOpenHandler)
- Delegate signature:
- C# Example: the Open event on Excel application object
- Delegate signature:
public delegate void
AppEvents_WorkbookOpenEventHandler(Wb Workbook); - Variable declaration:
public void MyWbkOpenHandler(Excel.Workbook wb)
{ // Do stuff… } - Connect handler:
app.WorkbookOpen +=
new AppEvents_WorkbookBeforeOpenEventHandler(
MyWbkOpenHandler)
- Delegate signature:
- The "My Button Stopped Working" issue
- Happens when a Click event handler fails to respond
- "The cause of this issue is connecting an event handler to an object whose lifetime does not match the desired lifetime of the event." (Carter & Lippert: VSTO 2007, p. 33)
- Generally occurs because of issues with the object to which you are connecting the event handler…
- Goes out of scope
- Gets set to
null
- Be especially careful when adding controls to a command bar, ribbon, or action pane
- Do not declare these controls as locally scoped objects!
- Instead, be sure to declare them as class-scoped member variables
- Collisions between method names & event names
- Example: Excel has an
Activate
event & anActivate()
method - Remember that an Office object against which you are programming will often implement several interfaces
- One interface will contain method definitions & a different one will contain event definitions
- As with any .NET object implementing multiple interfaces which contain similarly-named members you handle the name-clash issue by casting the implenting object to the interface whose member you want to implement
- In VSTO things work a little differently
- No explicit casting is necessary when invoking a method
- Explicit casting is required to invoke a name-clashing event
- VSTO event interface naming convention:
OjectNameEvents_Event
- Example:
WorkbookEvents_Event
- Syntax/example:
// various using statements…
using Excel = Microsoft.Office.Interop.Excel;
namespace WbkEventListener
{
class WbkListener
{
Excel.Workbook myWbk;
public WbkListener(Excel.Workbook workbook)
{
myWbk = workbook;
}
public void ConnectEvents()
{
((Excel.WorkbookEvents_Event)myWbk).Activate +=
new Excel.WorkbookEvents_ActivateEventHandler(Activate);
}
public void Activate() { // event handling code… }
}
}
- Example: Excel has an
- General
- Properties
- The Office Primary Interop Assemblies (PIAs)
- General
- Office apps all written in unmanaged code that exposes COM interfaces
- VSTO in part a set of .NET wrapper classes
- These classes are compiled into PIA(s)
- Could theoretically create your own wrapper classes using .NET tool called TLBIMP (though they would then be called IAs, not PIAs)
- Reasons not to create your own Office IAs
- Your classes wouldn't be compatible with anyone else's
- The PIAs have their own 'special fixes'
- And, of course, why re-invent the wheel?
- Installing the PIAs
- When doing a custom installation of Office (at least as of Office 2007) the PIAs are an optional installation, listed as
- There are, in fact, a number of Office PIAs
- PIAs are installed in the GAC
- Referencing the PIAs
- tab
- Select either
- This will add references to
Microsoft.Office.Core
VBIDE
(the PIA for VBA)- Either
Microsoft.Office.Interop.Word
Microsoft.Office.Interop.Excel
- Interesting 'translation'
- When selecting the appropriate reference in the
Excel.exe
file (note PATH in tab) tab you are directed to, say, the - However, after making your selection(s) if you look at the tab in VS you will see that the reference is to PIA managed object in the GAC
- In particular, your Excel reference will be
Microsoft.Office.Interop.Excel.dll
- Also, a reference to the Microcosft Office xx.0 Object Library (
office.dll)
is added automatically
- When selecting the appropriate reference in the
- Note: PIAs are referenced automatically when creating VSTO-template projects
- Browsing the PIAs
- Excel
Application
object in COMApplication
classApplication
coclass- Coclass defines interfaces that a COM class implements
- Coclass contains:
_Application
(the primary interface)AppEvents
( a dispinterface - i.e., a set of properties & methods on whichIDispatch::Invoke
can be called)
- How TLBIMP translates Excel coclass, & its 2 interfaces, into .NET types
Application
coclass itself- Becomes a .NET class,
ApplicationClass
- Also becomes a .NET interface, not renamed - i.e.,
Application
Application
interface has no methods or properties of its own- Derives from the two other interfaces associated with the coclass:
_Application
AppEvents_Event
- Becomes a .NET class,
_Application
interface - directly imported into .NET- Generally not used
- Exception: when you need to resolve name clashes between methods & events
- Explicitly cast methods to
_Application
interface - (Events explicitly cast to
AppEvents_Event
interface)
- Explicitly cast methods to
AppEvents
dispinterface- Winds up in .NET but is not useful because it must be processed further
- That reprocessing creates
AppEvents_Event
- TLBIMP also creates several helper types
AppEvents_Event
- Like
AppEvents
- However, methods are replaced with delegates & events
- Like
- Multiple
AppEvents_*EventHandler
where*EventHandler
= COM method name AppEvents_SinkHelper
(this can be ignored in VSTO programming)IAppEvents
- Imported directly because it is a public type in the Excel type library
- However, effectively a duplicate of
AppEvents
- Difference between
IAppEvents
&AppEvent
:AppEvents
declared as a dispinterface in the type libraryIAppEvents
declared as a dual interface in the type library
- End result (& comments)
- Interfaces (5)
_Application
- Direct import from type library
- Generally not used, except to resolve name clashes between methods & events
AppEvents
- Direct import from type library
- Not used
AppEvents_Event
- Created from processing AppEvents event interface
Application
interface derives from this- Generally not used, except to resolve name clashes between methods & events
Application
- Created from the
Application
coclass - Use!
- Created from the
IAppEvents
- Dual interface version of
AppEvents
in the type library - Not used
- Dual interface version of
- Delegates (29) - All delegates take the form
AppEvents_*EventHandler
- Created from the
AppEvents
event interface - Use these when declaring delegates to handle events
- Created from the
- Classes (2)
AppEvents_SinkHelper
- Created from the
AppEvents
event interface - Ignore
- Created from the
ApplicationClass
- Created from the
Application
coclass - Not used (except that this class makes it appear that you can new an Application interface)
- Created from the
- Summary: "Two" types to use
Application
interface- The delegates
- Interfaces (5)
- Similar pattern occurs for the following Excel objects:
- Chart, OLEObject, & Workbook
- Worksheet object
- Works slightly differently from other Excel objects
- Worksheet coclass:
_Worksheet
(the primary interface)DocEvents
( a dispinterface - i.e., a set of properties & methods on whichIDispatch::Invoke
can be called)- Therefore, delegates for worksheet events called via
DocEvents_*EventHandler
- QueryTable object
- Also behaves somewhat differently
- QueryTable coclass:
_QueryTable
(the primary interface)RefreshEvents
( a dispinterface - i.e., a set of properties & methods on whichIDispatch::Invoke
can be called)- Therefore, delegates for QueryTable events called via
RefreshEvents_*EventHandler
- Excel
- General
- Office business applications (OBAs)
- Office Solutions Overview
- Three basic patterns of Office solutions
- General
- Office automation executable
- A program separate from Office
- Controls & automates an Office application
- Executable runs in its own process, not inside the Office process
- Add-In
- A class inside an assembly (
*.dll
) that Office loads as-needed - Runs in process with the Office app (versus requiring its own, separate process)
- Registered in Windows registry so Office app can find & start upon its own startup
- Unlike code behind a document, an add-in can remain loaded as long as Office app is running
- A class inside an assembly (
- Code-behind
- Note: VBA is a code-behind model
- Code is associated with a particular Word document or Excel workbook
- Other, advanced patterns
- Server document pattern
- Run code on a server that acts on an Office document
- The Office app itself is never started
- This is all made possible via cached data
- XML pattern
- Similar to server document pattern
- Inovlves writing Office docs in Open XML formats without ever starting an Office app
- Server document pattern
- Office automation executable
- Hosted Code (i.e., add-in & code-behind patterns)
- Discovery of hosted code
- Add-ins discovered because they are registered in the Windows registry
- Code-behind
- This approach does not require a registry entry
- Code is found via a special property added to the document itself
- Context provided to hosted code
- Hosted code needs to be able to find the
Application
object &Document
object into which it must load - How accomplished
- Add-Ins: via a class in the code which has a member variable representing the Office app
- Code-behind: via a class in the project which represents the host document
- Hosted code needs to be able to find the
- Entry points for hosted code
- For both Add-Ins & code-behind the
StartUp
event handler - This allows code to register for events that might happen later in the session
- For both Add-Ins & code-behind the
- Discovery of hosted code
- How hosted code runs after Startup
- Code runs in response to events raised by Office
- Code runs in response to controls provided by the hosted code
- How code gets unloaded
- Automation executable: when the executable exits
- Add-In
- When the Office app shuts down
- When the user 'manually' turns off the add-in
- Code behind: when the document is closed
- Catching code unloads
- For autmation execuatable you create your own notification
- For VSTO code-behinds & add-ins you can handle the Office
Shutdown
event
- General
- Office automation executables
- General
- Default: Office applications are launched in invisible mode
- Code is unloaded when the executable exits
- Note: you always need 2
using
statements:
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Forms;
- Setting up in VS
- You start a console or Windows application, not a VSTO application
- Add a reference to the appropriate application, browsing under the COM tab
- Even though you browse to COM object VS converts the reference to a reference to the appropriate .NET Interop assembly
- VS also includes related .NET interop assemblies (e.g., to
Microsoft.Office.Core
)
- Lifespan of Office app inside automation executable
- Automation executable controls Office app by holding that app in a variable
- Reference count
- Each Office app keeps track of how many clients are holding a reference to that Office app object
- Reference count incremented by one when automation executable news that app
- Attempting to close an Office app when its reference count is non-zero
- Excel
- Excel will appear to exit
- However, if you check Task Manager/Processes you will see Excel remains
- Word - will close/exit
- Excel
- When variable holding Office app inside the automation executable goes out of scope & is garbage collected
- Office app should exit, as its reference count goes to zero
- However, I found that not to be the case
- Note: Apparently an application's
Visible
property determines whether or not the app exits when its variable is garbage collected inside the executable
- Yielding time to an Office app inside an automation executable
- This effectively keeps the executable open/running as long as the Office app is open & running
- Need a reference to
System.Windows.Forms
inside assembly - Set a class-level boolean (probably called "exit") & a default value of
false
- C# code:
while (exit == false)
System.Windows.Forms.Application.DoEvents(); - VB code:
While exit = False
System.Windows.Forms.Application.DoEvents()
End While - Allow
exit
to be set to True via some event-handler- Event-handler is in Office automation code
- Event-generator is in the Office app - e.g., an "Exit" button
- Syntax/example:
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Forms;
// other using statements…
namespace MyConsoleApp
{
class Program
{
static bool exit = false;
static void Main(string[] args)
{
Excel.Application myExcelApp = new Excel.Application();
myExcelApp.Visible = "true";
// Code you want to run…
while(exit == false)
System.Windows.Forms.Application.DoEvents();
}
// Some event handler that sets exit = true upon exiting…
}
}
- Attaching an executable to an already-running Office app
- Option 1: Attaching to the application object itself
- Use
System.Runtime.InteropServices.Marshal.GetActiveObject
- Pass in a string identifier called a program ID, aka ProgID
- A ProgID takes the form
[ApplicationName].Application
- Example: ProgID for Excel:
Excel.Application
- Note 1: when declaring the application object instantiate it with a value of
null
- Note 2:
GetActiveObject
returns anobject
that must be cast to the appropriate application object
- Use
- Option 2: Attaching to an Office document object
- Use
System.Runtime.InteropServices.Marshal.BindToMoniker
- Pass in full path of currently-open Office document
- Note:
BindToMoniker
returns anobject
that must be cast to the appropriate document object
- Use
- Note: both
GetActiveObject
&BindToMoniker
throw aCOMException
if the specified application/document are not running/open - Note: Look also at
Running Object Table
in MSDN library
- Option 1: Attaching to the application object itself
- Other Office automation details
- Project references
using
statements to add…using Office = Microsoft.Office.Core;
- Either
using Word = Microsoft.Office.Interop.Word;
using Excel = Microsoft.Office.Interop.Excel;
- A word about
Word.Application
&Excel.Application
- These are interfaces, yet in Office automation projects we
new
them - This works because the compiler knows that each "interface is associated with a COM object that it knows how to start" (Carter & Lippert: VSTO 2007, p. 64)
- Note: both Word & Excel will start in invisible mode, which may or may not be what you want
- These are interfaces, yet in Office automation projects we
- Closing Word (& Excel??)
- Once you make the Word visible control over its lifetime shifts from your app to the user
- Therefore, unless you specifically code your app to close Word before exiting Word will continue to run
- If Word is never made visible it will be closed when your controlling app closes
- General
- Hosted code
- Office add-ins
- General
- Detected & loaded by Office app when that app starts; remains loaded through lifetime of the app
- Context: provided via a class in the project that has a member variable representing the app being customized
- How code is unloaded
- Office app exits
- User turns off the add-in
- VSTO add-ins
- Note: Add-ins remain installed on your system after exiting VS
- To remove an add-in, open VS & run
Build/Clean Solution
- COM add-ins
- Can create a class library project, implementing the
IDTExtensibility2
interface - Register the class/library in the registry as a COM object & add-in
- VS includes Shared Add-in project to create
- Limitations vs. VSTO add-ins (esp. re: Office 2003 & Office 2007):
- Lifetime issues (Office applications don't shut down)
- Isolation issues (add-ins clash with one another)
- (COM add-ins are sometimes called shared add-ins)
- Can create a class library project, implementing the
- Automation add-ins for Excel
- Managed classes that expose public functions that Excel can use in formulas
- Must register in the registry as a COM object
- Smart Documents add-in (VSTO does a much better job handling Smart Document functionality)
- General
- Code-behind
- General
- Not registered in Windows registry
- Context: provided via a class in the project that has a member variable representing the document being customized
- Code is unloaded when document is closed
- VSTO details
- When specifying a VSTO project
- VSTO creates classes for developer to use
- Classes have pre-hooked-up context & events
- Word: 1 code-behind class (corresponding to the document)
- Excel: multiple code-behind classes
- Workbook
- Each worksheet or chart sheet
- Code-behind classes derive from a base class to extend all of the methods, properties & events of the container object
this
keyword refers to the container/containing Office object- In VS Office document appears as a designer window - just as when working with, say, Windows Forms
- When specifying a VSTO project
- General
- Office add-ins
- Three basic patterns of Office solutions
- Office Programming
- Office Programming in .NET
- Excel
- Programming Excel
- Ways to customize Excel
- Automation executable
- VSTO add-ins
- In XL, when navigating to
dialog box
- You are in fact shown both VSTO & COM add-ins
- Can tell which is which by file extension (show in
line in dialog box)
- COM add-ins are
*.dll
files - VSTO add-ins are
*.VSTO
files
- COM add-ins are
- However, a user cannot add a VSTO add-in using the dialog box
- User must instead run
*.vsto
orsetup.exe
file - Add-ins are posted to the registry
- Both VSTO & COM add-ins are under the
HKEY_CURRENT_USER\Software\Microsoft\Office\Excel\Addins
key - Excel also checks for COM add-ins under the
HKEY_LOCAL_MACHINE\Software\Microsoft\Office\Excel\Addins
key- COM add-ins registered under the
HKEY_LOCAL_MACHINE
key cannot be turned on or off by user - It is recommended that you not register COM add-ins here because they are hidden from the user
- COM add-ins registered under the
- Both VSTO & COM add-ins are under the
- In XL, when navigating to
dialog box
- Automation add-ins
- VSTO code behind
- Smart Documents & XML expansion packs
- Smart documents involve:
- Attaching an XML schema to a workbook or template
- Associating code with that schema
- This combination of schema & code is called XML Expansion Pack
- Associating an XML expansion pack with a given workbook
- Within XL:
- Note: these menu choices are only available if an XML Expansion Pack has been created
- Smart documents involve:
- Smart Tags
- Smart tags = pop-ups which are activated based on text entered in a workbook
- Creat smart tags via
- Smart Tag DLL
- VSTO code-behind document
- VSTO add-in
- Smart Tag DLL
- Incorporates
- Recognizer
- Associated actions
- A small triangle is shown in cell when recognizer recognizes text
- Incorporates
- To mange smart tags
- Then check or uncheck individual recognizers
- XLA add-ins
- Many VSTO features do not work when a customized workbook is saved an an XLA file
- MSFT recommends against developing XLA files using VSTO
- Server-generated documents
- Research Services
- Employs Excel's Research task pane
- Available under tab
- You write a web service to enable this
- Programming user-defined functions
- General
- Typically must create a DLL called an XLL
- Excel, however, does not support XLLs written in managed code
- Building a managed automation add-in that provides user-defined functions
- However, you can create UDFs without utilizing XLL
- See Carter & Lippert: VSTO 2007, pp. 99-101, for an example
- The method involves registering the DLL
- The method also involves utilizing a number of .NET attributes
- Additional user-defined functions
- Debugging user-defined function in managed automation add-ins
- Deploying managed Automation add-ins
- General
- The Excel object model (see Carter & Lippert: VSTO 2007, pp. 108-14 for a review)
- Ways to customize Excel
- Excel events
- General
- New workbook & worksheet events
- Double-click & right-click events
- Calculate events
- Change events
- Follow hyperlink events
- Selection change events
- WindowResize events
- Add-In install & uninstall events
- XML import & export events
- Before events
- Before close events
- Before print events
- Before save events
- Open events
- Toolbar & menu events
- Additional events
- Events in VSTO
- Excel objects
Application
objectWorkbooks
collectionWorkbook
objectWorksheets, Charts
, &Sheets
collectionsDocumentProperties
collectionWindows
collectionWindow
objectNames
collection &Name
objectWorksheet
objectRange
object- Special Excel issues
- Programming Excel
- Word
- Programming Word
- Word events
- Word objects
- Outlook
- Programming Outlook
- Ways to customize Outlook
- General
- Scope of the Outlook object model as of Office 2007
- 158 objects
- More than 4,000 properties & methods
- More than 300 events
- This is somewhat misleading
- 23 events are duplicated over 16 Outlook objects
- Object models that were required to Pre-Office 2007 Outlook
Collaboration Data Objects
Exchange Client Extensions
- No longer needed
- Primary way to code against Outlook is via add-ins
- Scope of the Outlook object model as of Office 2007
- Automation executable
- Add-Ins
- Smart Tags
- Custom property pages
- General
- The Outlook object model
- Wrap-up
- Ways to customize Outlook
- Outlook events
- Events in the Outlook object model
- Application-level events
- General
- Startup and quit events
- Activation events
- New Window events
- Window events
- Close events
- View and Selection Change events
- Folder Change events
- Context menu events
- Form region events
- Outlook item events
- Item addition, Deletion, Change, and Move events
- Copy, Paste, Cut & Delete events
- Property Change events
- Open, Read, Write, & Close events
- Item Load, Unload, & BeforeAutoSave events
- E-Mail events
- Attachment events
- Custom Action events
- Other Events
- Wrap-up
- Outlook objects
- General
- The
Application
object- General
- Methods & properties that return active or selected objects
- Properties that return important collections
- Performing a search & creating a
Search
folder - Copying a file into an Outlook folder
- Quitting Outlook
- The
Explorers
&Inspecors
collections - The
Explorer
object- General
- Working with the
Selected Folder, View & Items
- Working with an
Explorer
window - Adding buttons & menus to an
Explorer
window - Working with the
Navigation Pane
associated with theExplorer
window - Associating a
Web View
with aFolder
- Setting & changing the
Active Search
for anExplorer
window
- The
Inspector
object- General
- The Outlook
Item
assciated with theInspector
- The
Inspector
window - An
Inspector's
Word editor - Adding buttons & menus to an
Inspector
window
- The
Namespace
object- General
- The root folders of the open Outlook stores
- Another way to iterate over
Open Outlook Stores
xx
xx
< xx >
- Adding & removing
Outlook Stores
- Managing the Master Category list
- Checking whether Outlook is offling
- Getting standard folders - e.g., the Inbox folder
- Getting a folder, Outlook
Item, Store,
orAddressEntry
by ID - Determining the current user & an intoduction to
Recipient, AddressEntry, ContactItem, ExchangeUser, & ExchangeDistributionList
- Accessing address books & address entries
- Displaying the
Select Folder
dialog box - Displaying the
Select Names
dialog box
- The
Folder
object- General
MAPIFolder
&Folder
- Other identifiers for a
Folder
- Getting the
Store
for aFolder
- Accessing
Subfolders
contained in aFolder
- Accessing items in a
Folder
- A
Folder&s
view settings StorageItem
objects- Moving or copying a
Folder
to a new location - Displaying a
Folder
in anExplorer
view
- The
Items
collection- General
- Iterating over
Items
- Finding an
Item
- Adding an
Item
to anItems
collection - The
Table
object
- Properties & methods common to
Items
- General
- Creating an
Item
- Identifying the specific type of an
Item
- Other properties associated with all
Items
- Moving or copying an
Item
to a new location - Deleting an
Item
- Displaying an
Item
in anInspector
view - Working with built-in & custom properties associated with an
Item
PropertyAccessor
: accessing advanced properties- Saving an
Item
- Showing the
Color Categories
box for anItem
Mail
properties & methods
- Programming Outlook
- Excel
- Office Programming in VSTO
- VSTO Programming Model
- The VSTO programing model for documents
- General
- Separation of data & view
- Model-View-Controller
- Benefits of separation
- VSTO extensions to Word & Excel document objects
- General
- Aggregation, inheritance, & implementation
- Hooking up the aggregates
- Obtaining the aggregated oject
- Aggregation & Windows Forms controls
- Improving C# interoperability
- The Tag field
- Event model improvements
- Dynamic ontrols in the document
- General
- The Controls collection
- Enumerating & searching the collection
- Adding new Word & Excel host controls dynamically
- Removing controls
- Dynamic controls information not persisted
- Advanced topics
- Class hookup & cookies
- Inspecting the generated code
- General
- Startup & shutdown sequences
- The
Globals
class in Excel
- VSTO extionsions to the Word & Excel object Models
- General
- Word
- General
Document
class- Bookmark host control
- XMLNode & XMLNodes host control classes
- Content control classes
- Excel
Workdbook
host item classWorksheet
host item classChart
sheet host item &Chart
host controlNamedRange, XmlMappedRange
, &ListObject
host controls
- The VSTO programming model for add-ins
- VSTO document features in application-level add-ins
- Creating worksheets dynamically
- The VSTO programing model for documents
- Using Windows Forms & WPF in VSTO
- Working With…
- Document-level action panes
- Application-level custom task panes
- Outlook form regions
- The Ribbon
- Smart tags
- VSTO Data Programming
- Customizing data-bound documents
- Excel
- Defining a data source (VS steps)
- Activate window (available under tab)
- Click link
- This lauches
- In panel: select icon
- In panel: select icon
- In
panel either:
- Select an existing connection from the drop-down
- Click button
- In
panel:
- Almost always leave the box checked
- Generally accept the default name for the connection string
- In
- Select tables, views, and/or stored procedures
- When selecting tables you can specify columns to include!
- Using drag-and-drop designer tools to create data-bound controls
- The
window
- Tables which include foreign key references will be listed at least twice
- As a table unto itself
- As a child node of any/all reference tables
- This second feature allows creation of master-detail views
- Tables, columns, views & stored procedures have icons next to them
- The icons represent the default control created by dragging the tiem onto a spreadsheet
- Defaults:
- Tables: list object
- Columns: named range
- Clicking on an item exposes a drop-down showing all possible controls that can be created with & bound to the object
- Note: When dragging a column onto a spreadsheet as something other than (its default) a named range:
- You get both the control you specified and a label control
- The label control can be deleted without hampering the data binding of the remaining control
- Tables which include foreign key references will be listed at least twice
- Whenever you drag controls onto a spreadsheet you wind up with items in the component tray
- When dragging a table, for instance, VS will populate component tray with:
- Dataset
- Table adapter manager
- Binding source
- Table adapter
- If you are creating a master-detail view you can/will wind up with multiple instances of these componenets
- When dragging a table, for instance, VS will populate component tray with:
- The
window
- Defining a data source (VS steps)
- Word
- Very similar to binding controls to XL
- See Carter & Lippert: VSTO 2007, pp. 889-91, for further details
- Excel
- Datasets, adapters, & sources
- Data sources & security best practices
- Datasets
- Adapters
- Using binding sources as proxies
- Data-bindable controls
- Alternate technique for creating data-bound documents
- General
- Complex & simple data binding
- Data binding with Word documents
- Data caching
- Caching data in the data island
- General
- Caching your own data types
- Dynamically adding & removing cached membersfrom the data island
- Using
ICachedType
- General
- Manipulating the serialized XML directly
- Caching data in the data island
- Advanced ADO.NET data binding
- Binding-related extensions to host items & host controls
- General
- Extension to the list object host control in XL
- Using data binding & dynamic controls from an application-level add-in
- Customizing data-bound documents
- Server Data Scenarios
- ClickOnce Deployment
- General
- Document vs. add-in solutions
- Document solutions more complex to deploy
- With document solutions must handle both document & customization locations
- Installation options other than ClickOnce
- Third-party tools
- Windows Installer
- Document vs. add-in solutions
- Prerequisites (generally installed by ClickOnce installer)
- Office 2007 (duh…)
- Windows Installer 3.1
- Automatically included as a prerequisite by ClickOnce installer
- Note: if installer finds this missing & has to install it the user will have to reboot his machine
- .NET Framework 3.5 (or greater) SP1 or .NET Framework Client Profile
- ClickOnce installer will handle installation
- New option with VSTO 2008 SP1:
- Can specify the Client-Only Framework subset - aka, the .NET Network Client Profile
- Smaller footprint than full .NET 3.5 framework
- See discussion of The .NET Client Profile Runtime in C# 2010
- MSFT Office Primary Interop Assemblies (automatically included as a prerequisite by ClickOnce installer)
- Visual Studio Tools for the Office System 3.0 Runtime (automatically included as a prerequisite by ClickOnce installer)
- Deploying Add-Ins
- Publish locations
- for set-up files for VSTO solution
- whence the user will install the VSTO solution
- Reasons for the two, separate locations
- You might be handing off the creation of an installation package to IT department
- If publishing to a local web server
- You need to supply users with a url
- You, however, would be installing to what amounts to a regular, network folder
- Install settings
- VS will create a
setup.exe
that will install checked prerequisites - "To support the installation of the PIAs, Visual Studio copies some additional files alongside
setup.exe
in a folder calledOffice2007PIARedist
." (Carter & Lippert: VSTO 2007, pp. 961-2) -
- Default value is 7 days
- Selecting
can cause performance issues
- Suppose you have multiple add-ins, each from a different vendor
- If each add-in checks every time you start your office program it will have to hit each of the relevant urls
- Reminder:
- Updates are checked for only when the Office program starts up
- In other words, if user leaves PC on & Office program open indefinitely no updates will be installed
- VS will create a
- Publish version
- Can differ from version number of primary executing assembly in solution
- Pertains to deployment manifest
- This number is used to determine if an update is available
- Publishing
- The root publishing folder
- VSTO deployment manifest (
*.vsto
)- User can double-click to install
- However, that only works if user has all prerequisites already on machine
- Note: to roll back to an earlier version of the app simply copy the deployment manifest from the appropriate version sub-folder of the
Application Files
folder
setup.exe
(use if you are missing prerequisites)
- VSTO deployment manifest (
Application Files
folder- Contains sub-folders for each app version
- Inside each sub-folder:
- Deployment manifest file for that version
- If inside the latest version folder the deployment manifest is the same as the one in the root publishing folder
MyAddIn.dll.deploy
file: the assembly built by the add-in project (VS adds the*.deploy
extension)MyAddIn.dll.manifest
: the application manifest
- Can delete obsolete version sub-folders if you are sure you will never need to roll back to that version
- Never delete the most current version folder, though
Office2007PIARedist
folder- Contains an
o2007pia.msi
file - This is the file which installs the Office 2007 PIAs
- To execute this file either:
- Double-click it
- Execute
mslexe.exe /i O2007pia.msi
from command prompt - Wrap the file in another setup package
- Contains an
- The root publishing folder
- Post publishing
- All files & folders in the Publishing folder should be copied to Installation folder
- Users who do not have prerequisites: run
setup.exe
- Users who do have prerequisites: run
MyAddIn.vsto
- Users who do not have prerequisites: run
- Will also want to have the solution signed by appropriate folks in IT
- All files & folders in the Publishing folder should be copied to Installation folder
- Installing
- Users missing any of the prerequisites (should use
setup.exe
)- Note: Will get an error message if they click
MyAddIn.vsto
& VSTO runtime is not installed - User will get a prompt to accept EULA (End User License Agreement) for each missing prerequisite
- Note: Office 2007 PIAs do not have a EULA, but user is still prompted to accept their installation
- Dialog boxes show progress of each download & install
- Reboot is required if Windows Installer 3.1 was not present
- Note: Will get an error message if they click
- Installing on your development machine: caveats
- Publishing always invokes a build on dev machine (&, of course, you may well be creating builds as part of development)
- In turn, builds "register" both Add-Ins & document solutions on your (dev) machine
- This, however, is different from a full ClickOnce install
- Not installed in ClickOnce cache
- Instead, registered in the
bin\debug
directory of the VSTO project
- Running an install creates confusion with a solution is both installed & registered
- Two cases where this all becomes an issue:
- You want to test the installer on your dev machine
- Make sure to run for your solution
- This unregisters the solution & obviates problem of simultaneous registration & installation
- You have already tested installer on your dev machine & now want to develop some more
- You must uninstall the solution first
- Use dialog box
- You want to test the installer on your dev machine
- Additional issue with Excel solutions
- Must close Excel document designer in VS before installing solution on dev machine
- Otherwise the install will fail silently
- After all prerequisites are installed downloading & installation of VSTO customization begins
- User will be prompted to confirm customization installation
- Setup then runs security checks
- Note: an unsigned solution actually is signed, but VS signs it with a test certificate
- Behavior by installation source
- Intranet site: User is prompted whether solution not signed by trusted or test certificate
- Internet site: Default behavior is that user is prompted for install only if the solution is signed by a trusted certificate
- Users missing any of the prerequisites (should use
- Installation: The Aftermath
- Add-Ins should now show in tab of appropriate Office app
- Note: As of Office 2007 Office does not recognize the Publisher property of VSTO add-ins
- Add-Ins are placed in the registry
- Installer creates key in
HKEY_CURRENT_USER\Software\Microsoft\Office\Excel*\Addins
- *orWord
, etc. - Info in the key:
- Description
- FriendlyName
- LoadBehavior
- Equals something called a DWORD
- Can be bitwise
- Could also be:
0
- Disconnected/do not load1
- Connected/load add-in2
- Load at startup (i.e., when the host app starts)8
- Load on demand (i.e., not loaded until host app requires the add-in)16
- Connect first time (i.e., first time the user runs the host app)
- Common settings
- Typically set to
3
16
&18
used with load-on-demand scenarios, which often/typically occurs with Ribbon customization
- Typically set to
- Manifest
- Shows location of deployment manifest
- Same as Installation Folder URL set in ClickOnce page
- Installer creates key in
- Add-ins also show up in dialog box
- Impact from identifying solution as trusted
- Decision logged in VSTO inclusion list
- Inclusion list is in registry
- Key
HKEY_CURRENT_USER\Software\Microsoft\VSTO\Security\Inclusion
- Inclusion key is created for each trusted solution, & includes:
- GUID identifier
- PublicKey of the solution
- URL of the deployment manifest
- Key
- Location of add-in dll
- ClickOnce uses ClickOnce cache
- Located where app files are stored
- Generally you do not need to worry about this folder
- Exception
- You include local data files that you need to access & modify at runtime
- Use
DataDirectory
property ofSystem.Deployment.ApplicationDeployment
class (inSystem.Deployment
namespace)
- Updating
- Make changes to code, then publish using ClickOnce
- After publishing, copy all files & child folders from publishing location to installation folder
- Note: If you had set update-checking frequency to, say, 7 days & have now set it to check every time
- The new version will not install until 7 days have run
- Only then will updates be checked for every time
- Updating dialog box
- Gives user a chance to cancel out of an update taking a long time
- If a user does cancel VSTO will try to update the customization each time that customization loads, until the update is allowed to run
- Variations on the deploying add-ins theme
- Can deploy to an intranet web server
- Can deploy to an internet web server
- This, however, requires a publisher certificate
- For user, ClickOnce security looks for that certificate before it will download an add-in from the internet
- Can deploy to a CD or USB key
- Updates not supported under this methodology
- Must select a drive on dev machine as publishing location
- Leave text box blank
- Lastly, can use MSI-based installer instead of ClickOnce to deploy
- Publish locations
- Deploying document solutions
- Differences vs. deploying add-ins
- Customized document contains an embedded link to deployment manifest
- Customization installation is begun when doc is opened & that manifest is detected
- Caveats
- All prerequisites must already be installed
- ClickOnce security rules apply - esp. means customization will not install if located on internet & only signed with a test certificate
- "The location from which the document is opened is considered before a VSTO customization is allowed to install and run." (Carter & Lippert: VSTO 2007, p. 982)
- A document opened from an e-mail message is automatically considered to be an untrusted location & will not run
- A document opened from an untrusted network location is automatically considered to be untrusted & will not run
- Customized document contains an embedded link to deployment manifest
- Publishing a document solution to a web server (running IIS 7)
- Dev machine prerequisites
- Go to
- In
dialog box:
- Under check
- Under check
- Under check
- This all allows VS to manage configuration of web site for you
- Must also run VS as an administrator! (though this limits you to saving project to your local drive!!)
- Publishing steps
- Open window (right-click on project node in )
- On tab click on ellipsis (…) next to text box
- On resulting dialog box click on icon
- In ensuring window select
- Click on icon (upper R-hand corner)
- In
dialog box
- Provide
- Navigate to (or create) a publishing folder
- the selected/created folder
- Back on tab provide
- Note: VSTO will not create Publishing or Installation subfolder with your project name - create them yourself
- Copy files from Publishing to Installation folder
- Dev machine prerequisites
- Installing a document solution from a web server
- Three options
- Run the
setup.exe
file- Must do this if prerequisties are not installed
- After running
setup.exe
must still copy the document to a local machine (or to a trusted intranet site) before opening
- Launch the
MyApp.vsto
deployment manifest (not recommended for document solutions) - Open the document directly, which installs the customiztion
- Run the
- Opening a VSTO solution directly from an untrusted intranet site will fail - with prompts, though
- Three options
- Document installation aftermath
- When you explicity trust a document an enty is made in the Inclusion List
- Customiztion is added to the ClickOnce cache
- The customiztion will show up in Control Panel's
window
- Uninstalling a customization
- Removes it from ClickOnce cache
- Removes it from list of installed programs
- Next time you open a document after uninstalling its associated customiztion
- Office will try to reinstall the customization
- This is because document still has an embedded link in the deployment manifest
- Uninstalling a customization
- Whate does not happen after installation
- Except for inclusion list your registry is untouched
- Document itself is not stored anywhere - user controls this
- Office Trusted Locations list not directly affected
- Updating a document solution from a web server
- When a customization updates user will see a window if the update will take longer than a few seconds
- Updates only update the customization
- In other words, the document itself is not updated
- This means that you need to be careful that your update will not fail in older versions of the document
- You always have the (potentially cumbersome) option of having your update dynamically alter older documents
- Variations on the deploying documents theme
- If you deploy to an internet rather than intranet web server you must have a publisher certificate
- "…ClickOnce security setting require an[sic] publisher signature before a user is prompted to install a customization from the internet zone." (Carter & Lippert: VSTO 2007, p. 995)
- What is more, Office requires documents to be copied from internet to a trusted zone before ti will open that document
- CD or USB
- Updates are not supported this way
- When running publish leave text box blank
- After publishing then copy to a CD or USB
- Can deploy to a SharePoint server (see below)
- Deploy via a MSI-based installer
- If you deploy to an internet rather than intranet web server you must have a publisher certificate
- Differences vs. deploying add-ins
- ClickOnce Security
- Certificates
- General/test certificates
- Test certificates are listed in
- Test certificate given same name as project, with
_TemporaryKey.pfx
appended - In
tab or project's window
- Make sure box is checked
- VSTO apps must always be signed (apparently Windows Forms apps do not)
- Create a new test certificate if the test certificate in an old project has expired (certificates are only good for 1 yr)
-
button
- Use for full info on a certificate
- A self-certificate will, ipso facto, show the same issuer & issuee
- License to Code
- Need a publisher certificate
- Publisher certificate
- Issued by a certifying authority - aka, a CA
- Identifies authority
- Analagous to state DMV issuing you a driver's license
- Publisher certificates can be revoked by a CA!
- Publisher certificates also have expiration dates
- Obtaining a publisher certificate
- Common CAs
- Verisign
- GlobalSign
- thawte
- If app will only be used inside organization can act as your own CA by installing Microsoft Certificate Server
- Use
certmgr.exe
utility to manage your cerificates
- Common CAs
- Signing a deployment manifest with a publisher certificate
- Typcially you have to hand over deployment manifest to IT & have them sign it
- Manifests are signed using Mage
- From VS command prompt can fire up via either
mage.exe
- for command-line toolmageui.exe
- for ui-based tool
- Using Mage UI version
- As of VS 2008 there is a bug in Mage
- Must first open deployment manifest, which is an xml file, and remove the
<pubisheridentity>
(empty) element - Select & open deployment manifest
- Default file filter for Mage does not show
*.vsto
files, so change filter to*.*
- From VS command prompt can fire up via either
- Signing the manifest once in Mage
- Mage signs manifest once you save changes to manifest
- To navigate to certificate you want to use:
- Choose
- In dialog box check &
- Then browse to requisite
*.pfk
file
- Remember to copy
*.vsto
file to installation folder when done!
- Trusting a publisher certificate
- Optimal approach: add publisher certificate to users' list of trusted certificates
- Otherwise user will continue to receive trust prompts
- To add certficate to user's machine
- Typically done via a group policy
- To add a certificate manually:
- Navigate to (within IE Tools menu or from Control panel)
- Select tabs
- Click button & navigate to certificate
- General/test certificates
- Trust prompting
- You can actually customize when a user is prompted to trust a certificate
- Trust promping is controlled by zone
- Internet
- Untrusted Sites
- My Computer
- Local Intranet
- Trusted Sites
- Can see what sites are included in each zone (except My Computer) in dialog box
- Trust promping options for each zone - & associated registry key string values
- Disallow trust prompting -
Disabled
- Allow trust prompting only if solution has a publisher certificate -
Authenticode
- Allow trust prompting for both publisher & test certificates -
Enabled
- Disallow trust prompting -
- Can edit registry to overriding default trust prompting for any zone
- In
regedit
navigate to:\HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\.NETFramework\Security\TrustManager\PromptingLevel
- Note: you may have to add that key to the registry
- In
- Office trusted locations
- Can specify an http:// path only if the server supports either
- Web Distributed Authoring and Versioning (WebDAV)
- FrontPage Server Extensions Remote Procedure Call (FPRPC)
- Note: SharePoint document library sites meets these requirements
- Can modify trusted locations by group policy - best approach if distributing VSTO solutions in an enterprise
- Can specify an http:// path only if the server supports either
- Inclusion list
- "When a user makes a trust decision due to a prompt, that trust decision must be recorded." (Carter & Lippert: VSTO 2007, p. 1004)
- See Impact from identifying solution as trusted, above
- Note:
PublicKey
in the inclusion list entry correspongds toRSAKeyValue
element in VSTO deployment manifest - Can edit the inclusion list programmatically
- Use
AddInEntrySecurity
class (inMicrosoft.VisualStudio.Tools.Office.Runtime.Security
namespace) - Class is in the
Microsoft.VisualStudio.Tools.Office.Runtime.v9.0
assembly - For further info see MSDN article
- Use
- Security checks
- User machine runs through a series of 8 security steps before installing or running a VSTO solution
- In order, steps are:
- 1) Is the document on the local machine or in a trusted location?
- Applies only to document solutions
- If NO
- Document itself will open
- Error dialog box will appear, saying that the customiztion cannot be loaded
- 2) Does the deployment manifest require full trust?
- VSTO code has access to Office object model - as such, very powerful
- There is no way to trust VSTO code partially
- Note: Web browsers, on other hand, only partially trust their "sandbox" code
- VSTO code always demands/requires full trust
- 3) Is the deployment manifest signed with a certificate in the untrusted publishers list?
- 4) Is the certificate in the trusted publishers list? (If YES solution automatically installs & runs)
- 5) Is the deployment manifest coming from IE's restricted zone? (If YES solution will neither install nor run)
- 6) Is trust promping permitted? (If NO solution will neither install nor run)
- 7) Is the solution already in the Inclusion List?
- System checks for 2 items
- Public key
- Location of deployment manifest
- If both items are in an entry in Inclusion List the solution is installed & run
- System checks for 2 items
- 8) Has the user selected
on trust prompt?
- If at this point nothing…
- Nothing has prevented the system from trusting the solution
- On the other hand, nothing has told the system is should trust the solution
- Solution is installed & run if user clicks on
- If at this point nothing…
- 1) Is the document on the local machine or in a trusted location?
- Certificates
- Other deployment scenarios
- Deploying to a web site other than IIS 7
- Must set MIME type for
*.VSTO
is configured properly - Associate
.VSTO
file extension with MIME type"application/x-ms-vsto"
- This is taken care of in IIS 7 & VS 2008 SP1
- Note: Earlier versions of IIS may not be configured correctly
- Must set MIME type for
- Deploying to a CD or USB key (See Can deploy to a CD or USB key, above)
- Deploying to SharePoint
- This gets a little tricky - primarily because you cannot create the requisite installation location sub-folders in a SP document library
- Workaround:
- Publish to an intranet location available to all SP users
- Upload the document to the root publishing folder of the SP document library
- Notes:
- Users will have to add path to SP document library as a trusted location
- If pushing document solution as a content type template users will also have to add that path as a trusted location
- Custom Windows Installer setups (see Windows Installer, above)
- Deploying to a web site other than IIS 7
- Editing manifests using Mage
- General
- Must have published solution before using Mage
- See Signing a deployment manifest with a publisher certificate, above, for details on opening Mage
- Make sure you are opening the deployment manifest in the latest version-specific folder
- Setting the product name, publisher name, & support url with Mage
- Select entry in list box to access fields
- Entry in text box is not the same as publisher certificate!
- Publisher & product name appear in dialog box
- Trust prompt dialog box & Mage settings
- If your solution is signed with a test certificate publisher will show as "Unkown Publisher"
- Publisher listing in dialog box refers to publisher certificate
- If you supply a support url the name of your solution becomes a hyperlink to that url
- Mage needs to sign manifest when you save it
- You can sign with your test certificate
- Again, remember to copy your .VSTO file back to root publishing folder!
- Setting the friendly name & description
- Where friendly name is used:
- Trust prompt dialog box
- Office add-in management dialog boxes
- Must edit both
- Deployment manifest
- Application manifest - i.e., the
myApp.dll.manifest
file in your version-specific folder
- Procedure
- 1) Crack open the application manifest (an xml) file
- Place appropriate content in
friendlyName
&description
elements - Save & close
- Place appropriate content in
- 2) Open the modified application manifest in Mage
- Will have to change filter to
*.*
to show your*.dll.manifest
file - Re-identify certificate you want to use
- Save & close
- Will have to change filter to
- 3) Open the deployment manifest (
*.VSTO
file) in the root application folder- Select in list box
- Click button
- Browse to & select just-edited app manifest
- Save to re-sign
- 4) Copy the
*.VSTO
file back to version-specific folder, in case you ever have to roll back your version
- 1) Crack open the application manifest (an xml) file
- Where friendly name is used:
- Delay signing
- Sometimes you do not sign your solution until release
- Follow the above procedures
- Overwrite your delay-signed binaries in version-specific folder with full-signed binaries
- Then re-do steps 2 & 3
- General
- General
- VSTO Programming Model
- Bibliography
- Carter, Eric and Eric Lippert. Visual Studio Tools for Office: Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath. Upper Saddle River, NJ: Addison-Wesley, 2006. Print.
- Carter, Eric and Eric Lippert. Visual Studio Tools for Office 2007: VSTO for Excel, Word, and Outlook. Upper Saddle River, NJ: Addison-Wesley, 2009. Print.
- VSTO Overview
- C# 2010 & .NET 4.0 (in progress)
- General
- .NET Platform
- COM programming (i.e., pre-.NET Programming)
- General
- Emphasizes creating reusable binary code
- COM binaries often called COM servers
- No inheritance available with COM programming
- Note: can, however, declare has-a relationships
- Language-independence: COM binaries can be written using numerous languages
- Complexity of COM data type representation
- COM binaries often created via COM-aware frameworks
- Example: ATL (Active Template Library) - C++ - based
- No standardization of type system across languages & technologies that write/rely on COM binaries
- Example:
CComSTR
(ATL) !=String
(VB6) !=char*
(in C) - This makes building public methods very complicated & tricky in COM
- General
- Building blocks of the .NET platform
- General
- CLR (Common Language Runtime)
- Primary role: locate, load & manage .NET types
- Also takes care of low-level responsibilities such as memory management, security, and handling threads
- CTS (Common Type System)
- Specification that delineates all possible data types & programming constructs
- Covers how all these things interact & how they are represented in .NET metadata
- CLS (Common Language Specification)
- Not every .NET language will support every feature defined by CTS
- CLS is a subset of CTS & represents those data types & programming constructs which each .NET language must recognize & be able to work with
- CLR (Common Language Runtime)
- The base class libraries
- C# characteristics
- General
- Additional .NET-aware programming languages (see .NET Languages)
- .NET assemblies
- Overview
- General
- .NET compilers emit CIL, not platform-specific instructions
- .NET similar to Java bytecode in that it is compiled to platform-specific instructions last-minute
- Single-file & multi-file assemblies
- The Common Intermediate Language
- Benefits of CIL
- Compiling CIL to platform-specific instructions
- JIT compiler, aka Jitter
- Jitter called upon by .NET runtime has been optimized for machine's CPU & platform whenever runtime was installed
- Note: you can pre-JIT an assembly
- Use
ngen.exe
command-line tool - Can improve start-up time with graphically intensive programs
- Use
- .NET type metadata
- The assembly manifest
- General
- The .NET building blocks
- The Common Type System
- The five types
- Classes
- Interfaces
- Structures
- Enumerations
- Delegates
- Type members
- Intrinsic CTS data types
- The five types
- The Common Language Specification
- General
- CLS = minimal set of rules, standards, etc. a language must meet in order to be considered a .NET language
- Essentially a subset of CTS functionality
- CLS only applies to public members
- Ensuring CLS compliance
- Can use a .NET attribute to have compiler check for CLS compliance
- Syntax:
[assembly: System.CLSCompliant(true)]
- General
- The Common Language Runtime
- The Java runtime engine is called Java Virtual Machine (JVM)
- One runtime engine exists for all .NET-aware languages
- The binary:
mscoree.dll
- Aka, the Common Object Runtime Execution Engine
- Key base class library for runtime engine:
mscorlib.dll
- The Common Type System
- The Assembly/Namespace/Type distinction
- General
- Code libraries
- Many programming languages come with these (essentially these are utilities)
- .NET does not employ code libraries, though
- Instead, .NET employs language-neutral .NET libraries
- Organizing .NET libraries
- "A namespace is a grouping of semantically related types contained in an assembly." (Troelsen: 5th Ed., p. 27)
- A single assembly can contain (& in the base class libraries this is typically the case) multiple namespaces
- Any one namespace in turn typically contains numerous types
- Difference between .NET & other languages is that with .NET each language accesses the same namespace(s) & the same types
- See Table 1-3, Troelsen: 5th Ed., pp. 29-30, for a good summary of the most significant .NET namespaces
- Code libraries
- The MS root namespace (for Windows-based services)
- Accessing a namespace programmatically
- Employ the type-level
using
keyword - Can also use a fully-qualified name
- Employ the type-level
- Referencing external assemblies
- General
ildasm.exe
- Viewing CIL code
- Viewing type metadata (
ctrl-M
) - Viewing assembly metadata (aka The Manifest)
- Exploring an assembly using Reflector
- Overview
- Deploying the .NET runtime
- Full .NET Framework:
dotNetFx40_Full_x86.exe
(≈77 MB) - The .NET Client Profile Runtime
- Smaller/lighter version of .NET runtime (≈34 MB)
- Setup package:
dotNetFx40_Client_x86.exe
(≈77 MB)
- Note: 64-bit versions of both runtimes are also available
- Full .NET Framework:
- The platform-independent nature of .NET
- Common Language Infrastructure (CLI)
- Relates to deploying .NET in non-Windows environments
- CLI, along with C# language specifications, have been released to ECMA (specifications 334 & 335)
- CLI is the much larger specification & has 6 partitions
- CLI defines minimal set of namespaces, functionalities, etc.
- Two major implementations of CLI
- Mono project - for Linux & Max OS X / iPhone devices
- Portable.NET - attempts to maximize number of OSs
- Common Language Infrastructure (CLI)
- COM programming (i.e., pre-.NET Programming)
- Building C# Applications
- The .NET Framework 4.0 SDK
- General
- SDK is freely downloadable
- Must download if not using a MSFT IDE
- VS 2010 command prompt
- Using this avoids having to update computer's
PATH
variable - Entering
csc -?
at VS command prompt displays command-line arguments for C# command-line compiler
- Using this avoids having to update computer's
- General
- Building C# applications using csc.exe
- General
- When using command-line commands prefix options with either a dash (-) or a slash (/)
- You can compile C# applications from code written in NotePad
- Specifying input & output targets
- Use
/out
argument to specify name of assembly - Use various
/target
arguments to control what is being created - Syntax:
- At a minimum must specify the
*.cs
file you are compiling - Options precede the name of the file
- At a minimum must specify the
- Use
- Referencing external assemblies
mscorlib.dll
is automatically referenced during compilation process (i.e.,System
namespace)- Use
/reference:
(or/r:
) command-line flag for this - See The default response file re: automatically-referenced assemblies
- Referencing multiple external assemblies (use semi-colon delimited list)
- Compiling multiple source files
- Use space-delimited list
- Can also use the wildcard character (
*
) - e.g.,csc *.cs
- C# response files
- General
- Use to contain/collect various input options
- Take a
*.rsp
file extension - Denote comments with the
#
character - On command line precede the response file name with the
@
sign - Multiple
*.rsp
files- This is eminently do-able
- Use space-delimited list on command line
- Command-line options in later
*.rsp
files will override those in earlier such files
- Combining command-line options with
*.rsp
files- Can be done
- Whatever comes later overrides what went earlier
- Exception: the effect of the
/reference
flag is cumulative
- The default response file (csc.rsp)
- Located in same directory as
csc.exe
file itself - This is automatically referenced, even when supplying a custom
*.rsp
file - References numerous, common .NET assemblies via
/r:
flag /noconfig
flag disables reference tocsc.rsp
- Located in same directory as
- General
- General
- 'Other' development environments
- Notepad++
- SharpDevelop
- Visual C# 2010 Express
- VS 2010
- Utilities
- Solution Explorer utility (under
)
- Referencing external assemblies
- Viewing project properties
- Class View utility (under )
- Object Browser utility (under )
- Solution Explorer utility (under
)
- Integrated support for code refactoring
- Code expansions & surround with technology
- Snippets (inserts prefab code at cursor)
- Can insert via short-cut menu within code
- Can also insert by beginning to type relevant code and then selecting
Tab
twice
- Surround With technology (wraps code within given scope)
- Code snippets are saved in/as XML - ergo, you can author your own
- Snippets (inserts prefab code at cursor)
- Visual Class Designer
- In R-click on your project (not on the solution) to view
- You create a class diagram file (
*.cd
) as part of your project when doing this - Use with two other items in VS:
- Class Details window
- Select
- This window is opened automatically when you create a new class designer
- Class Designer Toolbox (select )
- Class Details window
- .NET Framework 4.0 SDK documentation system
- Utilities
- The .NET Framework 4.0 SDK
- .NET Platform
- Basic C# Programming Constructs
- General
- Anatomy of a C# program
- General
- "C# demands that all program logic be contained within a type definition…" (Troelsen: 5th Ed., p. 73)
- Ergo, global functions & global variables are not allowed
- The class that defines the
Main()
is called the application object - Multiple application objects
- They can be employed
- Useful for performing unit tests
- However, must either:
- Employ the
/main
option if utilizing the command-line compiler - In VS 2010 set the ( )
- Employ the
Main()
method- Must, of course, be declared as
static
- The
(string[] args)
parameter is the mechanism for accepting command-line arguments
- Must, of course, be declared as
- Variations on the
Main()
method- Can have
Main()
generate a return value (typically abool
orint
) - Can omit the
(string[] args)
parameter - Can define
Main()
method aspublic
(implicit/default modifier isprivate
)
- Can have
- Specifying an application error code
- Generally you have
Main()
returnvoid
- Conventions from C-based languages when returning a value
- Value of
0
indicates successful execution - Any other value (though typically
-1
) indicates an error - Note: even when return type is explicitly set to
void Main()
in fact returns a0
upon successful completion
- Value of
- Application error codes in Windows
- Application return value stored in
%ERRORLEVEL%
environment variable - Can obtain this value via
System.Diagnostics.Process.ExitCode
property - Note: Cannot get this error code in-process; only available after the application has crashed and burned
- Need to include a
return
statement to control the error code thrown off byMain()
method - See Troelsen: 5th Ed., pp. 76-7, for an example of invoking a CS program from within a DOS/batch file and then handling the return value
- Application return value stored in
- Generally you have
- Processing command-line arguments
- Can use
for
orforeach
commands - Can also use
GetCommandLineArgs()
method of theSystem.Environment
type- First index returned by the method is the name of the application itself
- Remaining items in returned array = the passed-in parameters
- Note: When using this approach including the
string[] args
as an input parameter forMain()
becomes superfluous (though not at all harmful!)
- Can use
- Specifying command-line arguments with VS 2010
- Fill in box under
- Delimit multiple input parameters with spaces
- General
- Two base classes of note
System.Environment
classSystem.Console
class- Basic I/O with the
Console
class (see Troelsen: 5th Ed., pp. 82-3) - Formatting Console output
- Use tokens, such as {0}, a placeholders for "variables"
- Always begin with 0
- A token can be used more than once in a string
- Tokens need not be used in order
- Formatting numerical data
- Can include various formatting characters, aka format tags
- Syntax (to generate "I have $99.00"):
Console.WriteLine("I have {0:c}",99);
- With some formatting characters you also include a digit to specify the number of decimals, etc.
- .NET includes a wide variety of other formatting types you can employ
- Formatting numerical data beyond Console applications
- Can use string formatting characters inside the
string.Format()
method - Example:
string myNmbr = string.Format("I have {0:c}",99);
- Can use string formatting characters inside the
- Basic I/O with the
- Data types
- System data types & C# shorthand notation
- General
- Note: not all C# data-types are CLS-compliant
- See Troelsen: 5th Ed., Table 3-4, pp. 86-7, for a full list of data types
- Summary
- Whole numbers
sbyte; byte:
-128 to +127; 0 to 255short; ushort:
-32K to +32K; 0 to 65Kint; uint:
-2.1B to +2.1B; 0 to 4.3Blong; ulong:
-9 Quint to +9 Quint; 0 to 18 Quint
- Decimal numbers - in increasing order of precision & size
float
double
decimal
- Whole numbers
- Two defaults to be aware of
- Floating point numbers
- These are treated as doubles by default
- To override that attach an
F
or anf
to the raw numerical value - e.g.,5.3F
- Raw whole numbers
- The default to
int
data types - Attach
L
orl
(e.g.,10L
) to force to along
type
- The default to
- Floating point numbers
- Numerical types map to structures in the
System
namespace - Structures are value types which are allocated on the stack
- Variable declaration & initialization
- Can declare & assign initial values in one statement
- Can declare multiple variables of the same type on one line (delimit declarations with a comma)
- Lastly, can declare any data type using its full
System.
name
- Intrinsic data types & the
new
operator- All data types have a default constructor
- Declaring a data type with the
new
keyword then assigns a default value to the variable - Syntax (2 examples):
bool b = new bool();
int i = new int();
- The data type class hierarchy
- Every type (of course) derives from
System.Object
- Ergo, every data type has methods such as
ToString(), Equals
, etc. - Numerical data types derive from
System.Value.Type
- Every type (of course) derives from
- Intrinsic data types
- Numerical data types
- All numeric types support
MaxValue
&MinValue
properties - Certain data types have other members: e.g.,
System.Double
supports methods for positive & negative infinity
- All numeric types support
System.Boolean
(supportsTrueString
&FalseString
properties)System.Char
- Both
char
&string
types are Unicode - Remarkable number of static methods available for the
System.Char
type
- Both
- Parsing values from
String
data- Use the static
Parse()
method of the underlying data type - Example:
bool b = bool.Parse("True");
- Use the static
System.DateTime
&System.TimeSpan
- These are among the few data types for which there is no C# keyword
- You do have to
new
these data types System.TimeSpan
in particular has a number of interesting members
- The .NET 4.0
System.Numerics
namespace- New namespace in .NET 4.0
- Defines a structure called
BigInteger
- (Namespace also defines a structure called
Complex
, which is designed to be used in mathematical modeling) - To use:
- Add a reference to
System.Numerics.dll
to your project - Add the appropriate
using
statement
- Add a reference to
- Note:
BigInteger
values are immutable - See Troelsen: 5th Ed., pp. 95-7, on how to create and manipulate
BigInteger
values
- Numerical data types
- General
- Working with string data
- Basic string manipulation
System.String
is essentially a utility class- Many methods can be called on a string variable itself
- However, some methods are static
- String concatenation
- In C# use the
+
symbol - CLR actually converts this symbol to a call to the
String.Concat()
method when compiling the CIL
- In C# use the
- Escape characters
- Escape characters begin with a backslash (i.e., "\")
- See Troelsen: 5th Ed. Table 3-6, pp. 100-1, for a listing
- Defining verbatim strings
- Apply the WYSWYG principle to strings
- Preface the string with the @ sign (it comes before the opening quotation mark defining the string)
- Verbatim strings preserve white space
- To use quotation marks inside a verbatim you must use two consecutive quotation marks.
- Strings & equality
- The normal approach with reference types (which is what strings are) is to determine equality by seeing if the references are pointing to the same object in memory
- With strings, though, that approach has been overridden; equality is based on the values of the string types
- You can invoke "dot" properties and methods right on a declared string
- Example:
int i = "Bill".Length;
- The immutability of strings
- Once declared the character data of string objects cannot be changed
- If & when you run string manipulation functions you are actually creating new
string
objects - If you assign a new value to a
string
variable the initial value remains on the heap until garbage collected - Significance of the immutability of
string
objects is that in a project with a lot of string concatenation & manipulation & such you can easily eat up a lot of memory
- The
System.Text.StringBuilder
type- This is the object of choice when faced with a lot of string manipulation
- Resides in the
System.Text
namespace - Must
new
a variable of theStringBuilder
class - Especially useful for creating somewhat longer or multi-line strings
- Build your text using
StringBuilder
'sAppendLine()
method - When done use the
ToString()
method
- Build your text using
StringBuilder
initially holds 16 chars or fewer- You can change that initial buffer size when new-ing the
StringBuilder
instance StringBuilder
does automatically expand its buffer size as needed
- You can change that initial buffer size when new-ing the
- Basic string manipulation
- Narrowing & widening data type conversions
- General
- Data type mismatches are never a problem when there is no possibility of data loss
- Example: You can pass a
short
as a parameter to a method which is expecting anint
- In this example the compiler automatically widens the
short
to anint
- Implicit widening is more formally referred to as an upward cast (again, there is no possibility of data loss).
- Implicit narrowing operations, on the other hand, always generate compiler errors, even when the values assigned to the variable would fit/work.
- When employing a narrowing operation you must make use of an explicit cast
- Syntax/Example:
byte myByte = 0;
int myInt = 200;
myByte = (byte)myInt;
- Trapping narrowing data conversions
- Obviously, you always run the risk of data loss with narrowing casts
- Use
checked & unchecked
keywords to test for data overflow conditions (e.g., abyte
value greater than 255). checked
keyword tells system to throw aSystem.OverflowException
when a value does not fit into the range of the underlying data type- At that point you can then program within a
try/catch
block (ignored in the next 2 examples) - Syntax 1:
byte myByte = 0;
int myInt = 200;
myByte = checked((byte)myInt); - Syntax 2:
checked
{
byte myByte = 0;
int myInt = 200;
myByte = (byte)myInt;
//…other lines of code
}
- Setting project-wide Overflow Checking
- Do not continually have to wrap your statements in a
checked
block - Can set the
/checked
flag in the command-line compiler - In VS click
- Employing this setting is especially useful when building and debugging code
- Disabling the setting, however, will increase the runtime performance of production release
- Do not continually have to wrap your statements in a
- The
unchecked
keyword- For obvious reasons it's rarely used
- When used it typically overrides the
checked
keyword in a limited portion of code
System.Convert
- Can either widen or narrow
- Syntax/Example:
byte myByte = 0;
int myInt = 200;
myByte = Convert.ToByte(myInt); - One benefit of this method is that it is language neutral (e.g., VB has a different syntax for casting)
- General
- Implicitly typed local variables
- General
- Use
var
"data type" to have compiler infer the variable's data type at runtime - Note:
var
is not technically a keyword- You can use "var" as a name for
parameters, variables & fields
- (Doing so, however, will lead to tears.)
- When
var
is used to specify a data type when declaring a variable compiler recognizesvar
as a keyword - To be precise,
var
is a contextual token
- You can use "var" as a name for
- Can use implicit typing for
arrays, generics, custom types
- for any data type at all
- Use
- Restrictions on implicitly typed local variables
- Can use
var
keyword only with local variables in method or property scope - Cannot use
var
in the following:- Return values
- To be more specific, you cannot define a method which returns a
var
- However, so long as there is a match in the underlying data types you can use an already-defined implicitly typed local variable following the
return
keyword within a method - See Troelsen: 5th Ed., p. 115, for examples & discussion
- To be more specific, you cannot define a method which returns a
- Parameters
- Field data inside a custom type
- Return values
- Must be assigned an initial value at the time of declaration
- Implicitly typed local variables &
null
- Cannot assign a value of
null
when declaring an implicitly typed local variable - Once you allocate & configure the variable, you can, however, then change the its value to
null
(assuming, of course, that the data type is a reference type)
- Cannot assign a value of
- Using implicitly typed local variables in assignment statements
- You can use another variable to assign the initial value to an implicitly typed local variable
- You can conversely use an implicitly typed local variable to assign an initial value to another variable
- Note: when doing the latter the variable whose value you are assigning can itself be an implicitly typed local variable
- Cannot use the C#
?
token with thevar
keyword - i.e., cannot declare asnullable
- Can use
- Implicitly typed data is strongly typed data
- At runtime an implicitly typed local variable still becomes strongly typed as soon as it is allocated & configured
- Note: this is different from using the
var
keyword in, say, JavaScript - Therefore, as with "regular" variables you will receive a compile-time error if you pass in a value that is of a different data type
- Usefulness of implicitly typed local variables
- Prima facie, the use of the
var
keyword seems like spectacularly bad programming practice in a strongly-typed programming language - Where the use of implicitly typed local variables is essential, however (and, about the only place where you want to use the
var
keyword) is to receive values with LINQ query expressions
- Prima facie, the use of the
- General
- System data types & C# shorthand notation
- Looping & branching
- C# iteration constructs
- The
for
loop- Syntax/Example:
for(int i =0;i < 4; i++)
{ /* …code to iterate over */ } - Note: the variable
i
is only visible within the iteration code
- Syntax/Example:
- The
foreach
loop- General
- Used to traverse through all members of an array or a collection
- Syntax/Example:
string[] beatles={"John", "Paul", "George", "Ringo"};
foreach(string b in beatles)
Console.WriteLine(b); - Note: when iterating over a collection the
foreach
keyword is used in conjunction with theIEnumerator & IEnumerable
interfaces
- The use of
var
withinforeach
constructs- Can definitely be done
- In practice, you only - but often must - do this in conjunction with LINQ
- General
- The
while
&do/while
looping constructs- No guarantee that
while
loop will execute even once (condition may not be met) - By contrast,
do/while
loop will execute at least once - See Troelsen: 5th Ed., p. 119, for syntax & sample code
- No guarantee that
- Keywords available within
for
loop (& other iterations):goto
- Directs code to a labeled part of code
- Just as with VBA, a label is a word followed by a colon (e.g.,
SkipToHere:
) - Note: this means you can also skip to
Case:
statements within aswitch
construct
continue
- jumps to the next iteration of your loopbreak
- breaks out of the loop
- The
- Decision constructs & the relational/equality operators
if/else
statements- Some languages allow their
if/else
statements to proceed based on whether a condition evaluates to, say, 0 or -1 - In C#
if/else
operates based solely on boolean expressions
- Some languages allow their
- The
switch
statement- Must include
break
keyword at the end of each section ofcase
code - This holds true even for the
default
statement switch
statement can operate on string as well as numeric results- See Troelsen: 5th Ed., pp. 121-2, for syntax & sample code
- Must include
- C# iteration constructs
- Method & parameter modifiers
- General
- Parameter modifiers precede data type keywords when specifying method arguments
- If a parameter is not marked with a modifier it is assumed to be passed by value
- Default parameter-passing behavior
- This means a copy of the data is given to the method
- Note: how that copy is handled will differ based on whether the parameter is a value or reference type
- Modifiers
- The
out
modifier- A method must assign a value to every output parameter (i.e., those marked with
out
keyword) - Failure to do so generates a compile error
- Within the method do not use
return
keyword to assign value to output parameter; simply assign a value - Calling methods:
- Must also make use of the
out
keyword when specifying arguments - Do not need to assign values to a variable before passing it in as an
out
parameter (since, after all, the called method must provide a value)
- Must also make use of the
- You can declare multiple output parameters within a single method
- A method must assign a value to every output parameter (i.e., those marked with
- The
ref
modifier- Use when you want a method to change the value of a data point
- You must initialize a variable before passing it in as by reference (unlike the case with
out
parameters)
- The
params
modifier- C# supports the use of parameter arrays
- This "allows you to pass into a method a variable number of identically typed values as a single logical parameter." (Troelsen: 5th Ed., p. 130)
- Syntax/example:
static int SumValues(params int[] value)
{
//Code to sum values…
} - Using the
params
keyword you can also can also send in a strongly-typed array of comma-delimited items- This latter approach tends to be more efficient than the former approach
- When you do this C# silently creates the array for you
- With the former approach you have to:
- Declare an array variable
- Populate that variable
- Pass the variable into the method
- Methods can only take a single
params
argument - A
params
argument must be the final argument
- The
- Defining optional parameters
- New feature as of .NET 4.0
- Feature is added primarily to provide easier compatibility with COM objects
- There is no
optional
keyword - Rather, to make a parameter optional you provide an initial/default value directly in the parameter-specification section of your code
- Further, that default value must be known at compile time
- Therefore, the following method signature will not compile:
static void EnterLogData(string message,
DateTime timestamp = DateTime.Now) - Optional parameters must always appear at the end of a method's signature
- Invoking methods using named parameters
- Also new as of .NET 4.0
- Also added to enhance compatibility with COM objects
- Allows you to invoke arguments in any order you choose
- Use
:
rather than VBA's:=
to specify a named parameter - If you mix named & unnamed parameters you must list all of your unnamed parameters first
- Using named parameters can be very helpful when dealing with a method with multiple optional arguments
- Method overloading
- Methods must differ by type and/or number of parameters
- Methods which differ only by return type cannot be distinguished by overloading
- Do not use
overloads
keyword to overload a method - Simply declare additional versions of the method
- General
- Advanced .NET "data types"
- Arrays
- General
- Elements in an array must be of the same type
- Note: because arrays are zero-based upper bound will be 1 less than the declared number of members
- If you declare an array & do not specify a value for a given index that item will be set to the default value of the data type
- Can use arrays as arguments or as return values
- C# array initialization syntax (4 sample approaches)
- Syntax 1 (using the
new
keyword & size):
string[] arrFabFour = new string[4]
{ "John", "Paul", "George", "Ringo" }; - Syntax 2 (using the
new
keyword, no size):
string[] arrMarxBros = new string[]
{ "Harpo", "Groucho", "Chico" }; - Syntax 3 (with neither size nor the
new
keyword):
string[] arrStooges = { "Mo", "Larry", "Curly" };
- Syntax 4 (using the
Array
keyword):
Array arrEnums = Enum.GetValues(someEnumVar.GetType());
- Syntax 1 (using the
- Implicitly typed local arrays
- Can use the
var
"data type" when initializing arrays - Items within array must still be of the same underlying data type
- You do not specify a data type with the
new
keyword - Syntax/Example:
var[] a = new[] { 1, 3, 5, 7 };
- Can use the
- Defining an array of Objects
- It can be done
- Effectively allows you to skirt the requirement that all items in an array be of the same type
- Multidimensional arrays
- Rectangular array
- Simply an array of multiple dimensions
- Syntax/Example:
int[,] my2DArray;
my2DArray = new int[6,6];
- Jagged array
- This is an array of arrays
- Syntax/Example:
//An array of 5 inner arrays…
int[][] myJagArray = new int[5][];
- Rectangular array
- The
System.Array
base class- Methods
Clear()
- static- Can specify starting point & number of elements to clear
- Items cleared are set to their empty/default values
CopyTo()
- staticReverse()
- staticSort()
- Works only on 1-dimensional arrays
- Works on all intrinsic types
- Will work on custom types which implement the
IComparer
interface
- Others
- Properties
Length
(number of items in the array)Rank
(number of dimensions in the array)- Others
- Methods
- General
- Enums
- General
- If you do not declare specific values enums are assigned sequential values of 0, 1, 2, etc.
- Syntax: When specifying members in an
enum
the list should be comma-delimited
- Controlling the underlying storage for an
Enum
- Default data type used to store
enum
values isSystem.Int32
- Syntax/Example - for overriding underlying data type:
enum BeatleMemb : byte
{
John = 50,
Paul = 200,…
} - This can be useful when programming for a low-memory device like a phone
- Default data type used to store
- Declaring
Enum
variables - The
System.Enum
type- Custom enumerations derive from the
System.Enum
class type - Will frequently use the static
GetUnderlyingType()
static method ofSytem.Enum
- This method does not take an enumeration as a parameter
- Instead, this method expects to be passed a
System.Type
- Two options for generating this
System.Type
parameter:- If you want to use a variable of your enumeration type take advantage of
GetType()
, which, of course, is "always" available, as it is a method ofSystem.Object
:
Enum.GetUnderlyingType(variableOfYourEnumType.GetType())
- If you want to use a reference to your enumeration type itself, simply use the
typeof()
method:
Enum.GetUnderlyingType(typeof(yourEnum))
- If you want to use a variable of your enumeration type take advantage of
- Custom enumerations derive from the
- Discovering an
Enum
's name/value pairs- For enumerations
ToString()
has been defined to give you the name of the enumeration value - Can cast an enumerated variable against the underlying data type to get the underlying value
- Can now easily construct a
for
loop to give you the information - Note: there are also two other useful (static) methods
Enum.Format()
Enum.GetValues()
- This returns a 1-dimensional
System.Array
- See Troelsen: 5th Ed., pp. 148-9, for sample code using this method
- This returns a 1-dimensional
- For enumerations
- General
- Structures
- General
- More than just a user-defined type for creating a collection of name/value pairs
- Can contain data fields
- Can contain methods which operate on those fields
- Cannot inherit from structures, though
- Creating structure variables
- If you do not
new
a structure when creating it you must assign values to field data before invoking any method which utilizes the field(s) - On the other hand, if you do new a structure the default constructor will assign default values to all fields
- You can create custom constructors
- If you do not
- General
- Value types & reference types
- General
- Structures have no identically-named entity in the .NET library - i.e., there is no
System.Structure
type - Structures derive (implicitly) from
Sytstem.ValueType
- As a value type, all structures are allocated on the stack rather than on the (garbage-collected) heap
- Items on the stack are removed from memory as soon as they go out of scope
- A closer look at
System.ValueType
- Essentially exists only to override the virtual methods of
System.Object
- Purpose of overriding these methods is to create value-based implementations (
System.Object
methods are reference-based) - Instance methods of
System.ValueType
are identical to those ofSystem.Object
:
public abstract class ValueType : object
{
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual string ToString();
}
- Essentially exists only to override the virtual methods of
- All numerical data types, plus enumerations & custom structures, are value types
- Structures have no identically-named entity in the .NET library - i.e., there is no
- Value types, reference types, & the assignment operator
- "When you assign one value type to another, a member-by-member copy of the field data is achieved." (Troelsen: 5th Ed., p. 155)
- When working with numeric data types you are simply copying the value
- When working with structures the values of all fields are copied to the new structure
- Example:
int i = 10;
int j = i;
i=15;
// … j is still == 10
- Post-assignment behavior of reference types (i.e., all classes) is very different, though
- The assignment operator changes or redirects what a reference variable points to in memory
- In the above example, if
ints
were classes, thenj
would also be equal to 15 after changing the value ofi
to 15 - This is because setting
j = i
simply means "havej
point to whatever it is on the heap thati
is pointing to"
- "When you assign one value type to another, a member-by-member copy of the field data is achieved." (Troelsen: 5th Ed., p. 155)
- Value types containing reference types
- You can & might create a structure where an internal field is of a reference data type
- Assume you do just that & and set
struct2 = struct1
- Changes in any of the
value
fields of 1 struct will still not affect the value of that field in the other struct - However, a change in any property of the embedded reference-type field in one struct will be reflected in the other struct
- When you set
struct2 = struct1
the embedded reference fields then contain pointers to the same item in memory - This is also known as creating a shallow copy
- Changes in any of the
- Note: To create a deep copy you need to implement the
ICloneable
interface when creating your struct or value type
- Passing reference types by value
- This situation tends to arise when you have to supply a reference type as a parameter to a method
- Reminder: When creating a method if no appropriate keyword is provided parameters are passed by value!
- What gets passed is a copy of the reference to the caller's object
- Ergo, the method receiving the passed-in parameter can then invoke methods & change properties of that object (i.e., calling object will see the results of those changes)
- What the receiving method cannot do, however, is to cause the caller's reference to point to a completely new or different object
- Passing reference types by reference
- Called method can still, of course, change the object's state
- In addition, called method can also change the item to which the original variable is pointing to
- See Troelsen: 5th Ed., Table 4-3, pp. 161-2, for a good summary on differences between value & reference types
- General
- C# nullable types
- General
- A value of
null
indicates an empty object reference - Ergo, value types can never take the value of
null
- Note: as reference types, strings can take a
null
value - Since .NET 2.0 you have, however, had the ability to create nullable data types
- Typically most convenient when working with database values
- You create a nullable type by appending a question mark (
?
) to the end of the data type- You can only use this syntax with value types (it's obviously redundant to do so with reference types)
- Local nullable variables must still be assigned a value before they can be used
- This syntax is shorthand for creating an instance of the generic
System.Nullable<T>
structure type - i.e.,Nullable<bool>
is synonymous withbool?
- Note: This inheritance exposes some members not available to regular value types
- A value of
- Working with nullable types
- You can declare a nullable type as the return value of a method
- If you define, say, variable
i
to be a nullable int, you can see whether or not it== null
withi.HasValue
boolean property
- The
??
operator- Sometimes you want to assign a "default" value that will "override" assigning a value of
null
- Could of course construct an
if
statement around the variable'sHasValue
property - Shortcut syntax:
SomeDbReaderClass dr = new SomeDbReaderClass();
//Assume class has a method for returning a nullable int…
int myInt = dr.GetSomeNullableIntValue() ?? 100;
- Sometimes you want to assign a "default" value that will "override" assigning a value of
- General
- Other
- Random
- Syntax:
random r = new Random();
- Random types have a number of methods:
Next()
- Overloaded
- Returns an
int
NextDouble()
- Syntax:
- Random
- Arrays
- Arithmetic shortcut notations
- Increment/decrement
- Post-increment/-decrement
- This uses the 'original' value of a variable in an expression, & then changes the value by 1:
- To increment a variable by 1:
i++;
- To decrement a variable by 1:
i--;
- Pre-increment/-decrement
- This first changes the value of a variable & then uses that changed value in an expression
- To increment a variable by 1:
++i;
- To decrement a variable by 1:
--i;
- Post-increment/-decrement
- To increase the value of one variable by the amount of another:
x += y;
- To decrease the value of one variable by the amount of another:
x -= y;
- Increment/decrement
- Anatomy of a C# program
- Encapsulated Class Types
- The C# class type
- General
- Field data comprise the state of a class
- Classes, of course, also contain members (including constructors) that operate on these data
- Note: In VS you typically create an individual
*.cs
file for each class in your code
- Allocating objects with the
new
keyword- Allocating (i.e., new-ing) an object is what, in fact, creates the object in memory
- Declaring a variable of your object type only creates a pointer that refers to the object in memory
- General
- Creating classes - basic
- Constructors
- General
- Used to assign values to an object's internal fields
- Constructors never have a return value - not even
void
- Constructors always take the name of the class they are identifying
- The default constructor
- Always exists (invisibly) if you do not create another constructor
- Sets fields to their default data type values
- Can be redefined
- Defining custom constructors
- Can define multiple constructors for a given class
- As always when overloading a method, these must differ by type or number of arguments
- The default constructor - revisited
- Default constructor is silently removed as soon as you create a custom constructor
- If you then still want the default constructor you must explicitly redefine it
- General
- The
this
keyword- General
- Gives you access to the current class instance
- Use to resolve scope ambiguity where you've been foolish enough to have a method parameter with the same name as a class field
static
class members- You will get a compile error if you attempt to use
this
keyword inside a static member - That is because
static
members are those which only operate at the class level, & thethis
keyword pertains to an instance of the class
- You will get a compile error if you attempt to use
- Chaining constructor calls using
this
- Constructor chaining is a very useful technique when you have multiple constructors
- Very often constructors perform some validation on incoming parameters
- Without constructor chaining you would have to repeat that validation code inside each constructor
- You could pull the validation code into its own method & have each constructor call that validation method, but even that is ultimately redundant
- Optimal approach:
- Put all validation code inside the constructor that takes the greatest number of arguments
- Have additional constructors call the master constructor by use of the
this
keyword - These other constructors then pass, say, default values for parameters where appropriate
- Syntax (Car example):
//1 constructor…
public Car(string name) : this(name,0) {}
//Master constructor…
public Car(string name, int speed)
{
//validation - e.g., if speed > 80… - etc.
}
- Constructor flow
- When chaining constructors be aware that there is a call stack
- A called constructor has control returned to it after it has invoked a master constructor
- Therefore, the called constructor can still execute code after invoking a master constructor
- Optional arguments - revisited
- As of .NET 4.0 constructors get easier still
- One can define a single constructor & make any & all input parameters optional
- Especially useful when used in conjunction with named arguments
- General
- The
static
keyword- General
- Static members must be invoked on the class itself, not on an object reference
- "Simply put, static members are items that are deemed (by the class designer) to be so commonplace that there is no need to create an instance of the type when invoking the member." (Troelsen: 5th Ed., p. 181)
- Static methods are most typically found in "utility" classes
- Static methods
- Can only work with static fields/data
- Call only call fellow-static methods
- Static field data
- Non-static data is called instance data
- If a class has non-static field each object of the type has its own copy of the field
- Obviously, you declare a field as static if its value will be common to all objects
- Static constructors
- If you have a regular constructor affect static data then that value will be changed for all objects of the type every time you instantiate a new object!
- Often, though, you want to be able to set the value of a static field at runtime, rather than at design time
- C# allows you to do this via a static constructor
- Characteristics/syntax of a static constructor:
- You can only define a single static constructor (i.e., it cannot be overloaded)
- You do not use any access modifiers
- There are no parameters allowed
- It executes only once, regardless of the number of objects created
- The runtime invokes the static constructor either
- When it creates an instance of the class
- Before the first (i.e., any) static member is invoked by a caller
- It executes before any instance-level constructors
- Static classes
- A static class, of course, cannot be
new-ed
- If a class is defined as static all fields & members must be marked with the
static
keyword - If for some reason working pre-.NET 2.0 framework (when static classes were introduced) you create "static" classes by:
- Qualifying the default constructor as
private
(ergo, you cannotnew
that class) - Qualifying the class as
abstract
- Qualifying the default constructor as
- A static class, of course, cannot be
- General
- Constructors
- OOP
- Defining pillars of OOP
- Encapsulation
- Simplifies by hiding code
- Protects data
- Inheritance
- Use to establish is-a relationships
- Allows you to extend functionality
- Under code-reuse banner you also have has-a relationships
- (Has-a relationships have to do with containment & delegation rather than with inheritance)
- Polymorphism
- Relates to treating related classes/objects in a similar manner
- Polymorphic interface is the set of members available to all descendants of a class
- Create a class's polymorphic interface via constructing
- Virtual members
- Provide a default implementation
- May be changed/overridden
- Abstract members
- No default implementation provided
- However, a signature for the member is provided
- Must be overridden
- Virtual members
- Encapsulation
- C# access modifiers
- General
public
- Applies to types or type members
- Means: no restrictions
private
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: can only be viewed/accessed by the class or structure which defines the item
protected
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: access/visibility restricted to the type itself and any child class(es)
internal
- Applies to types or type members
- Means: only visible/accessible within the current assembly
protected internal
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: visible/accessible within:
- Defining assembly
- Defining class
- Derived classes
- Default access modifiers
- Types:
internal
- Type members:
private
- Types:
- Access modifiers & nested types
- Available modifiers for nested types:
private
protected
protected internal
- Ergo, non-nested types can only take the following modifiers
public
internal
- Available modifiers for nested types:
- General
- The first pillar: C#'s encapsulation services
- General
- Encapsulation based on protecting an object's internal data
- To manipulate object data want to force user to do so via:
- Accessor (a.k.a., getter)
- Mutator (a.k.a., setter)
- Use either of two methods to protect data:
- Define a pair of accessor/mutator methods
- Define a .NET property
- Encapsulation using traditional accessors & mutators
- Assume you have an
Employee
class with a privateempName
field - "Classically" you would write something like the following methods to read/write the employee's name
- Accessor:
public string GetName()
{ return empName; } - Mutator:
public void SetName(string name)
{
//Some validation code…
empName = name;
}
- Accessor:
- Assume you have an
- Properties
- Encapsulation using .NET properties
- In .NET you want to use properties rather than traditional get/set methods
- Syntax/Example:
public string Name
{
get { return empName; }
set
{
//some validation code…
empName = value;
}
} - Remember not to use parentheses to declare properties
- You have
get
scope &set
scope inside the property's scope - Declared data type in property must of course match the data type of the underlying field
- Note: within
set
scopevalue
token is not a C# keyword but a contextual keyword - .NET properties tend to be easier to work with than getter/setter methods
- Example (increasing an employee's age by 1 yr with getter/setter methods):
joe.SetAge(joe.GetAge() + 1);
- Example (increasing an employee's age by 1 yr with .NET properties):
joe.Age++;
- Example (increasing an employee's age by 1 yr with getter/setter methods):
- Using properties within a class definition
- Typically you include validation code in almost all "set" statements of your properties
- Rather than duplicate validation code within your constructors, pass your constructor parameters through your property definitions
- In fact, you typically want to run all internal data-manipulation through your property definitions within all of a class's methods
- Another way of saying this: as a rule it is only the properties themselves that should be allowed to touch a class's private fields
- Internal representation of properties
- CIL creates
get_
&set_
methods behind the scenes to handle .NET properties - Therefore, you will get compile errors if you create traditional accessor/mutator methods & name them with
get_
&set_
prefixes
- CIL creates
- Controlling visibility levels of property
get/set
statements- Unless you specify otherwise
get/set
statements "inherit" the visibility level of the container property - You can, though, specify a different - though only more restrictive - level for a getter or setter
- Unless you specify otherwise
- Read-only & write-only properties
- Achieve by simply omitting the appropriate
get/set
statement - If you make an property read-only then the only way to assign a value to the underlying field is through a constructor
- In that case you would allow a constructor access to the underlying field (i.e., rather than running the parameter through the associated property)
- Achieve by simply omitting the appropriate
- Static properties
- Must operate on static data
- Example: a company name for a/the
Employee
class - Static properties can, of course, only be read & set at the class level
- Probably easier to create a static constructor than go through the process of explicitly setting a static property
- Encapsulation using .NET properties
- General
- Defining pillars of OOP
- Creating classes - advanced
- Automatic properties
- General
- Introduced with .NET 3.5
- Used for those instances where you are not creating or employing any validation code
- Syntax/Example (for an Employee's name & age)
public string Name { get; set; }
public int Age { get; set; } - Limitation: the name of the auto-generated private field behind the property is not visible within the C# code base
- Interacting with automatic properties
- Because the private backing fields are unavailable at the code level any members which interact with the "fields" will have to do so using the property names
- External interaction is unchanged
- Automatic properties & default values
- When using auto properties of numerical or boolean data types the properties are immediately assigned a default value
- However, if you use auto properties for, say, as an internal/nested class type, the hidden reference type will initially be set a value of
null
- This exposes you to the possibility of encountering a
null reference exception
at runtime if you try to invoke the property of the container class which "points to" the contained class - To ensure this doesn't occur you must instantiate the appropriate contained object(s) within the constructor of the container class
- Creating immutable (i.e., read-only) automatic properties
- Option 1:
- Omit the
set;
statement - Example:
public string Name { get; }
- The immutable property then can only be set in the class's constructor
- Omit the
- Option 2:
- Declare the
set;
statement asprivate
- Example:
public string Name { get; private set; }
- Property can still be set via the class constructor
- Property can also be set via a method
- Under this option the property is still considered immutable because the class's consumers cannot change it
- Declare the
- Note: Should you desire, you can also supply a more restrictive access modifier (typically
private
) to either or both theget;
and/orset;
statements
- Option 1:
- General
- Object initializer syntax
- General
- When working with other people's classes you often want to set a number of properties upon creating an object yet find that the existing constructors do not allow that capability
- Object initializer syntax allows you instantiate an object and set property values on one line of code:
- Use brackets - i.e., { and } - instead of parentheses when new-ing an object
- Enclose a comma-delimited list of property/value assignments inside the brackets
- Syntax/Example:
Point myPt = new Point { X = 30, Y = 40 };
- Behind the scenes
- First invokes default constructor
- Then assigns property values
- Calling custom constructors with initialization syntax
- It can be done
- Call a custom constructor, and pass in values as normal
- Then, just after that bit of code add the comma-delimited property values inside brackets
- Syntax/Example (where color has been added as a property):
Point myPt = new Point("Black") { X = 30, Y = 40 };
- Note - earlier example could have been rewritten as:
//Explicitly invoke the default ctor…
Point myPt = new Point() { X = 30, Y = 40 };
- Automatic properties & initializing inner types
- You don't want to create automatic properties for inner/nested types - you will wind up with
null
values - Instead…
- Declare fields of the inner/nested type
- Use a
new
statement to create a valid object for the field variable to point to - Example inside, say, a Car class:
// Now you will never encounter a null reference exception…
private CarRadio carRadio = new CarRadio();
// Set property…
public CarRadio Radio
{
get
{ return carRadio; }
set
{ carRadio = value; }
}
- In your calling code you are then free to:
- Create new instances of the type used by the property
- Assign those new instances to the property/ies
- With nested types you can wind up creating nested object initialization declarations! (see Troelsen: 5th Ed., p. 213, for an example)
- You don't want to create automatic properties for inner/nested types - you will wind up with
- General
- Constant field data
- General
- Set by use of
const
keyword - Constant field data are implicitly static
- Ergo, if you expose that data through a property that property must be static
- You can also define constants within members (i.e., methods)
- Set by use of
- Read-only fields
- Use
readonly
keyword - Constant data values must be known & set at compile time
- Values for read-only fields, on the other time, can be set at runtime
- Value can only be set within the scope of a constructor
- Attempts to set the value of a read-only field outside the scope of a constructor will result in a compile error
- Use
- Static read-only fields
- Unlike constant fields, read-only fields are not implicitly static
- You can, of course, set the value at compile time
- If you are setting the value at runtime you must (of course) do so inside a (the) static constructor
- General
- Partial types
- Identified with
partial
keyword (must be used on every appropriate file/class definition) - If a class is large you can divvy it up across multiple
*.cs
files - Naming convention for files might be something like
MyClass.Internal.cs
, etc. - Typically, field data, constructors & properties are not modified much during development, so those are good candidates for an
*.internal.cs
file
- Identified with
- Documenting C# code via XML
- General
- Use triple-slash (
///
) notation to generate - See Troelsen: 4th Ed., Table 5-2, p. 177, for a list of recommended XML documentation elements
- Must include lines for:
- Opening XML tag
- XML content
- Closing XML tag
- However, VS will generate a lot of this commentary for you!
- Simply include
///
at the top of a class definition - VS will then generate opening & closing XML tags for
summary, parameters
, & (perhaps?) more
- Simply include
- XML comments can also be entered/created within VS's Class Details window
- Note: XML comments become visible from within VS's Intellisense
- Use triple-slash (
- Generating the XML file
- If using the command-line compiler (
csc.exe
) use the/doc
flag - In VS use the tab of the Properties window
*.xml
documentation files are created within the\bin\Debug
folder of your project
- If using the command-line compiler (
- Transforming XML code
- NDoc is one utility
- Can also use Sandcastle, which is a MSFT tool
- General
- Automatic properties
- The C# class type
- Inheritance & Polymorphism
- The basic mechanics of inheritance
- Specifying the parent class of an existing class
- Use a colon (
:
) to indicate inheritance - Establishes a is-a relationship (a.k.a., classical inheritance)
- A child class has access to all public members of its parent class(es)
- Note: Although constructors are typically defined as public they are not inherited by child class(es)
- Use a colon (
- Regarding multiple base classes
- C# does not allow for multiple inheritance
- (MI is allowed in certain other languages)
- Interfaces, however, allow for behavior that ultimately mimics MI very closely
- The
sealed
keyword- Sealed classes cannot be inherited
- You typically seal utility classes
- The
System
namespace, for example, contains a large number of sealed classes - Structures
- As noted before, structures are implicitly sealed
- Structures also cannot be derived from classes
- All in all, structures cannot be involved at all in inheritance; if you need inheritance you must employ classes
- Specifying the parent class of an existing class
- Revising VS class diagrams
- To work with class diagrams:
- Class diagrams are files unto themselves & take the
*.cd
extension - To create class diagrams simply drag from Solution Explorer window onto diagram surface
- Shortcut: To copy all types onto class diagram surface at once:
- Click on the project within Solution Explorer
- A button will appear in the Solution Explorer window - click on that icon
- Note: If you use automatic properties syntax in your class there (of course) are no underlying fields to see in a class diagram
- The second pillar of OOP: the details of inheritance
- General
- It's not uncommon to want to use a class you created in an earlier project within a current one
- To do so simple select
*.cs
file(s) & navigate to appropriate - Reminder: update the
namespace
declarations within the imported files
- Controlling base class creation with the
base
keyword- Inherent inefficiency of creating a derived class constructor without using the
base
keyword- Within a child-class constructor you would initialize any parameter which belongs to the base class by invoking properties
- However, if any properties of the base class are read-only you are out of luck
- Further, unless you override things C# invokes the base class constructor anyway before a derived class constructor is invoked
- Therefore, you have an inefficient construct, which calls two constructors, plus makes calls to any inherited properties
- Using the
base
keyword makes things much more efficient- Syntax:
public DerivedClass(datatype param1, datatype param2,
datatype param3,…) : base(param1, param2)
{
//Handle the params unique to derived class…
PropOfChildClass = param3;
} - Note the similarity in syntax with
this
keyword & constructor chaining - Now you have just one constructor call
- Note:
base
keyword can be used with any public or protected method of a parent class - its use is not limited to constructors - Remember that employing this syntax will silently remove the default constructor of the child class; if desired, explicitly recreate
- As a rule, this is how you want to create constructors in derived classes
- Syntax:
- Inherent inefficiency of creating a derived class constructor without using the
- Keeping family secrets: the
protected
keyword- Allows child data to access field data without having to use base-class properties
- This can increase efficiency - it also creates some obvious problems re: circumventing validation rules
- While you may want to keep fields private, it is very common to create methods as
protected
- Adding a sealed class
- General
- Programming for containment/delegation
- General
- This concerns has-a relationships
- Containment: adding a field/property where the data type is of some class
- Delegation: adding public members that expose the contained type's properties & methods
- Containment/Delegation is the Aggregation model in design patterns
- Understanding nested type definitions
- This is a variation on the has-a relationship
- C# allows you to define any of the 5 .NET types inside the scope of a class or structure
- The nested type can itself be declared as either
public
(if the containing class is public) orprivate
- Rationales for creating nested types:
- You gain complete control over the access level of an inner type
- You cannot declare a non-nested type as
private
- You can, however, declare a class as
private
if it is nested inside another type
- You cannot declare a non-nested type as
- A nested type can access the private members of its containing type
- If you are designing a utility/helper class that is only going to be used by one other type it is probably best to nest that helper class inside the class it is designed to assist
- You gain complete control over the access level of an inner type
- Accessing an inner class from outside the container type:
- It is certainly do-able - provided the inner type is identified as
public
- You must then essentially work with the inner type as a property of the container
- Syntax/Example:
static void SomeMethod()
{
OuterClass.PublicInnerClass inner;
inner = new OuterClass.PublicInnerClass();
}
- It is certainly do-able - provided the inner type is identified as
- Not surprisingly you can nest as many levels as you want
- General
- The third pillar of OOP: C#'s polymorphic support
- The
virtual
&override
keywords- Polymorphism allows for subclasses to define their own particular implementation of a parent/base class method
- Exact term for this is method overriding
virtual
keyword means that a method may (but does not have to be) overridden- If a subclass wants to take advantage of that opportunity it must employ the
override
keyword - Note: cannot mark a
static
method asvirtrual
- In VS when you employ the
override
keyword to declare a method:- VS automatically includes
base.MethodYouAreOverriding
within your method scope - Therefore, you do not need to recreate the underlying code
- VS automatically includes
- Overriding virtual members using VS 2010
- When overriding a method:
- Must re-use/re-declare all parameters & their types
- Must also re-declare any & all parameter passing conventions (i.e.,
ref, out, & params
)
- In VS, once you type
override
& then hit you are then presented with all overridable methods in your base class - When you then select a particular method & hit VS stubs out the method for you
- When overriding a method:
- Sealing virtual members
- You do this if at some point in the inheritance chain you don't want some member/method to be overridden any more
- You can override & seal all at once
- Syntax/Example:
public override sealed void SomeVirtualMethod() { /* code… */ }
- Abstract classes
- It's not uncommon to design a base class that you never want to see instantiated
- Often, a base class is simply too ill-defined to serve as an object
- In this case use
abstract
classes - these cannot be instantiated - Even when a class is abstract you typically want to give it constructors
- Abstract classes can certainly contain non-abstract members
- The polymorphic interface
- Abstract classes can contain abstract members
- An abstract member:
- Does not supply any default implementation
- Must be accounted for by any & all derived class(es)
- Note: an abstract member can only be declared inside an abstract class
- Elements of an abstract member
- Name
- Return type (if any)
- Parameter set (if any)
- Note: Since there is no implementation to provide you omit scope brackets
- Example/Syntax:
public abstract void MyAbstractMethod();
- A polymorphic interface is the set of an abstract class's abstract & virtual methods
- Handling abstract methods in child classes
- Mark with the
override
keyword to implement - If a child class is itself abstract then it does not have to implement its parent's abstract methods
- Mark with the
- Member shadowing
- Shadowing is when a child class defines a member with the same name used in a base/parent class
- This is most likely to occur when you are deriving from classes you, yourself, did not design
- 2 options when you want to shadow a member:
- 1) Use the
override
keyword- This, however, requires that you are able to modify the source code & mark the member you want to work with as
virtual
- Limitation, obviously, is that you often cannot modify the parent class's code
- This, however, requires that you are able to modify the source code & mark the member you want to work with as
- 2) Use the
new
keyword- The
new
keyword tells the compiler to ignore the base class's implementation of the method - You can in fact apply the
new
keyword to any derived type:field, constant, property,
etc.
- The
- 1) Use the
- Shadowing is also sometimes termed hiding a base-class member
- Lastly, if you decide you want access to the base-class member implementation after all? - Simply cast the derived object as an instance of the base class!
- The
- Base class/derived class casting rules
- General
- Base class for all classes in .NET is
System.Object
- You can always store a child class in a variable of some parent type - i.e., when the is-a relationship exists
- This is called an implicit cast
- Example:
//Assume Manager is derived from Employee…
Employee Bill = new Manager(Bill,…); - Note: Even though the variable
Bill
has been defined as anEmployee
type the object in memory to which it points is, of course, aManager
type - Benefit of implicit casting is that if you create a method which takes, say, an Employee as a parameter you can pass it a Manager, a SalesPerson, etc.
- As discussed earlier with datatypes, you also have explicit casts
- You must employ an explicit cast when you are going from parent class to child class
- Syntax/Example:
//To cast an Employee as a Manager…
(Manager)someEmployeeReference
- Base class for all classes in .NET is
- The C#
as
keyword- Casting is evaluated at runtime, not at compile time
- This obviously introduces the possibility of runtime errors if you make unworkable/inappropriate casts
- Can of course protect yourself by putting your cast statements inside
try/catch
blocks - Alternative: use
as
keyword & then test for anull
return value - Syntax/Example:
Manager bill = new Manager("Bill", other parameters…);
Circle c = bill as Circle;
if (c == null)
//Some code re: invalid cast…
- The C#
is
keyword- Returns
true/false
- Syntax/Example:
if (Bill is Manager) { //some code… }
- Returns
- General
- Master parent class:
System.Object
(incl.Person
example, Troelsen: 5th Ed., pp. 250-8)- General
- Virtual members
public virtual bool Equals(object obj);
- Default: return
true
if the items being compared refer to the identical item in memory - In other words, this compares object references, not the state of the object
- Method is generally overridden to test whether 2 objects have the same state (i.e., a value-based comparison)
- If you override
Equals()
you should also overrideGetHashCode()
! - Note: as discussed previously, this method is overridden in the
ValueTypes
class to make value-based comparisons
- Default: return
protected virtual void Finalize();
- Used to free unallocated resources before an object is destroyed
- This is covered in section on CLR garbage collection
public virtual int GetHashCode();
public virtual string ToString();
- Default: returns fully qualified name i.e.,
<namespace><type name>
- Often "overridden by a subclass to return a tokenized string of name/value pairs that represent the object's internal state…" (Troelsen: 5th Ed., Table 6-1, p. 252)
- Default: returns fully qualified name i.e.,
- Instance-level, non-virtual members
public Type GetType();
(a Runtime Type Identification - RTTI - method)protected object MemberwiseClone();
- Static members
public static bool Equals(object objA, object objB);
public static bool ReferenceEquals(object objA, object objB);
- Virtual members
- Overriding
System.Object.ToString()
- Recommended approach:
- Semi-colon delimited name/value pairs for every state in class
- Wrap entire list in square brackets - "
[
" and "]
" - This convention is followed by many types in .NET base class libraries
- Syntax/Example:
public override string ToString()
{
string myState;
myState = string.Format("[First Name: {0};
Last Name: {1}; …", FirstNm, LastNm, …]);
return myState;
}
- Note: when overriding
ToString()
should remember to include state data up the chain of inheritance- First run
base.ToString()
- Then append your sub-class's data
- First run
- Recommended approach:
- Overriding
System.Object.Equals()
- Assume we are overriding for value-based equality
- Since method takes a parameter of type
object
a smart programmer will check 2 things:- The object is the appropriate type
- The variable is not a
null
reference - Syntax/Example:
public override bool Equals(object obj)
{
if (obj is YourClass && obj != null)
{
YourClass temp;
temp = (YourClass)obj;
if (temp.Property1 == this.Property1 && …)
{ return true; }
else
{ return false; }
}
return false;
}
- In a large class with many fields this approach can be tedious
- Alternative:
- If you have a well-implemented
ToString()
method simply test equality of the returned strings - Because
ToString()
is a method ofSystem.Object
you don't even have to cast theobject
passed in as a parameter to see if it is of the correct type!
- If you have a well-implemented
- Overriding
System.Object.GetHashCode()
- A hash code is a numerical value that represents an object in a particular state
- Default: system uses object's current location in memory to generate hash value
- If you intend to store a custom class in a
Hashtable
type (this resides in theSystem.Collections
namespace):- You should override
GetHashCode()
Hashtable
invokes bothEquals()
&GetHashCode()
to identify correct object- Note:
GetHashCode()
is called to get general location of object;Equals()
is then invoked to determine exact object
- You should override
- Generating a hash code
- A variety of algorithms exist
- Easiest approach is to leverage the hash code algorithm behind
System.String
'sGetHashCode()
- If your class contains a string field that will be unique for each object (such as SSN of a
Person
object) simple do something likereturn SSN.GetHashCode()
- Failing that simply employ
return this.ToString().GetHashCode();
- Static members of
System.Object
- These, of course, cannot be overridden
- Especially when you have overridden the
Equals()
method in a custom class to check for value-based equalitySystem.Object.ReferenceEquals()
allows you to see whether two variables point to the same object in memory
- General
- The basic mechanics of inheritance
- Structured Exception Handling
- Overview: errors, bugs, & exceptions
- Defined
- Errors: user (generally input) errors
- Bugs: programming errors
- Exceptions: corrupted file, data connection not available, etc.
- .NET handles all 3 as exceptions
- .NET base class libraries contain a wide range of
Exceptions
- Note: .NET convention is to end all exception names with the word
Exception
- a custom which should be followed when creating custom exceptions
- Defined
- .NET exception handling
- General
- Error-handling in Windows programming prior to .NET used a variety of techniques
- Windows API itself identifies errors in a variety of ways
- COM developers often make use of interfaces to handle exceptions
- .NET introduced/makes use of structured exception handling (S.E.H.)
- Many other languages simply return a number to identify an error
- .NET exceptions are objects unto themselves & thus can contain a wealth of information
- The atoms of .NET exception handling
- 4 involved entities:
- "A class type that represents the details of the exception" (Troelsen: 5th Ed., p. 221)
- "A member that throws an instance of the exception to the caller" (Troelsen: 5th Ed., p. 221)
- The calling code
- A block of code within the caller that is designed to catch any thrown exceptions
- All .NET exceptions ultimately derive from
System.Exception
- 4 involved entities:
- The
System.Exception
base class- Important class components:
public class Exception : ISerializable, _Exception
{
//Ctors (partial list)…
public Exception(string message, Exception innerExcepion);
public Exception(string message);
public Exception();
//Methods…
public virtual Exception GetBaseException();
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context);
//Properties (partial list)…
public virtual IDictionary Data { get; }
public virtual string HelpLink { get; set; }
public Exception InnerException { get; }
public virtual string Message { get; }
public virtual string Source { get; set; }
public virtual string StackTrace { get; }
public MethodBase TargetSite { get; }
} - Comments:
- The
_Exception
interface allows a .NET exception to be recognized & handled by unmanaged code (esp. COM) - Many methods & properties are virtual & can thus be overridden
- The
- Important class components:
System.Exception
class propertiesData
- Retrieves a collection of key/value pairs (in an object implementing
IDictionary
) - Empty by default
- Programmer can populate with effectively unlimited info
- Retrieves a collection of key/value pairs (in an object implementing
InnerException
- Contains exception which caused "this" exception
- Prior exceptions are passed in via the constructor
Message
- passed in as a constructor parameterSource
- Name of the assembly or object that threw the current exception
- Read/write
StackTrace
- obviously invaluable in debuggingTargetSite
- The
MethodBase
object returned by property contains lots of information about the method which threw the current exception - Invoke
ToString()
onMethodBase
object to access the info
- The
- General
- A simple example (see Troelsen: 5th Ed., pp. 263-7)
- Throwing a general exception
- Can make use of
string.Format()
to setMessage
property when instantiatingException
object - Use
throw new Exception(…)
- Use a
try/catch
blocks in the calling code - Notes:
- In this approach you decide the circumstances which merit throwing an exception
- In practice, you of course really only want to throw an exception to address situations you cannot resolve within your code
- Can make use of
- Catching exceptions
- Syntax (of simple
catch
block):
catch(Exception e)
{ //Exception handling code… } - Code continues after the
catch
block - unless, of course, that block terminates things
- Syntax (of simple
- Throwing a general exception
- Exceptions
- Configuring the state of an exception
- General
- Not all properties can be set with a constructor
- 2 options:
new
theException
object, set properties, & then throw thatException
objectnew
theException
object using object initialization syntax
- The
TargetSite
property- The returned
MethodBase
object alone gives you the following info on the culprit-calling method:- Name
- Return type
- Parameter types
- Other useful properties of
MethodBase
objectDeclaringType
(name of class)MemberType
(Method, Property, etc.)- Syntax/Example:
e.TargetSite.DeclaringType;
- The returned
- The
StackTrace
property- Gives you full path of calling method
- Also gives you line numbers
- Bottom-most line of output = first calling method, etc.
- The
HelpLink
property- Empty string by default
- Use to provide link to a help file/url
- Cannot set this property inside the
Exception
constructor
- The
Data
property- Great, all-purpose property for adding custom data
- Cannot set this property inside the
Exception
constructor - Because you must invoke
Add
method of property in this case you must new theException
object before throwing it - Syntax/Example:
Exception ex = new Exception();
ex.Data.Add("Cause", "Some explanation…");
ex.Data.Add("TimeStamp",
string.Format("Occurred: {0}", DateTime.Now));
throw ex; - Must include a
using System.Collections;
statement in your namespace to iterate over results in acatch
block - Syntax/Example (for iterating over
Data
property incatch
block):
catch (Exception e)
{
if(e.Data != null)
{
foreach (DictionaryEntry de in e.Data)
//Process de values…
}
} - Note: despite all of the flexibility offered by the
Exception.Data
property, developers tend to build customException
objects (discussed below) when they want to include custom information
- General
- System-level exceptions (
System.SystemException
)- These are exceptions thrown by the .NET platform
- Typically regarded as fatal & non-recoverable errors
- Derives from
System.Exception
, but adds absolutely no functionality - Purpose of creating this class is to allow you to distinguish system errors from app errors
- Allows you to test errors (
if (e is SystemException)…
)
- Application-level exceptions (
System.ApplicationException
)- General
- Derives from
System.Exception
, but, as withSystem.SystemException
, adds absolutely no functionality - You should derive any custom exceptions from this class
- You tend to build these only "when the error is tightly bound to the class issuing the error…" (Troelsen: 5th Ed., p. 275)
- Derives from
- Building custom exceptions - possibilities
- Can always throw a
System.Exception
in your code - Often, however, it is more advantageous to throw a strongly-typed exception
- .NET Best Practices:
- .NET Best Practices: Any custom exception should end with the suffix "Exception"
- All custom exceptions should be
public
, so that they can be recognized when called outside of an assembly
- Possible approaches:
- Override the
Message
property (often usingstring.Format()
) - Add properties for information that you otherwise jam into the
Data
property
- Override the
- Note: assigning values to custom properties is a little easier than populating the
Data
property - Your
catch
block can now be written to catch an application-specific error
- Can always throw a
- Building custom exceptions - in practice
- You typically don't override the
Message
property - simply call on the message property via a call tobase
- You also typically don't add custom properties
- Simply build a custom class, which allows your
catch
block to immediately recognize source of error
- You typically don't override the
- Building custom exceptions - .NET best practices
- Requirements:
- Derives from
ApplicationException
- Is marked with
[System.Serializable]
attribute - Define the following constructors:
- Default (
public
) - One (
public
) that sets the inheritedMessage
property - One (
public
) to handle inner exceptions - One (
protected
) to handle the serialization of your type
- Default (
- Derives from
- See Troelsen: 5th Ed., p.276, for an example
- Note: VS has a code snippet template for all this - activate by typing
exception
and then pressing twice
- Requirements:
- General
- Configuring the state of an exception
- Handling exceptions
- Processing multiple exceptions
- General
- You can, & should, include multiple
catch
blocks, each one suited to a particular type of exception - When doing this arrange your
catch
blocks from the specific to the general, as the processor will stop at the 1stcatch
block that works - Note: when using multiple
catch
blocks you will in fact get a compiler error if your first block catches(Exception e)
, as now none of the othercatch
blocks are reachable - When using multiple
catch
blocks it probably doesn't hurt to make the last onecatch (Exception e)
- Best practice: be as specific as possible about the types of exceptions you are handling in your
catch
blocks
- You can, & should, include multiple
- General
catch
statements- You don't, in fact, have to specify the type of error you are catching
- Though not very useful, you can write:
catch { /* error handling code… */ }
- Rethrowing exceptions
- Typically done to push an error up the call stack
- Re-throwing is (of course) done inside a
catch
block - Syntax is simple:
throw;
(i.e., without any arguments) - this preserves the original error information
- Inner exceptions
- Though you really want to try your best to avoid this, it is, of course, possible to generate a new exception whilst in a
catch
block handling an existing problem - Best Practice (in this scenario):
- Catch the new error in an object that is of the same type as the original
Exception
- However, make the new error an inner exception of the new object
- Note: you create a new object because the only way to create an inner exception is as a constructor parameter
- Syntax/Example:
catch(MyCustomException e)
{
try
{ /* Some error-handling code… */ }
catch (Exception e2)
{
throw new MyCustomException(e.Message, e2);
}
}
- Catch the new error in an object that is of the same type as the original
- Though you really want to try your best to avoid this, it is, of course, possible to generate a new exception whilst in a
- The
finally
block- This block of code is optional
- Code that will still execute after an exception has been handled
- Use to close files, data connections, etc., etc.
- General
- Who is throwing what?
- .NET Framework 4.0 SDK documents any & all exceptions that any member of a .NET base class library can throw
- Inside VS, can also get this information by hovering mouse over base class member in code
- The result of unhandled exceptions (un-useful user prompt)
- Debugging unhandled exceptions using VS
- Run - code will break where exception is encountered
- You then get pop-up windows - is an especially useful link
- Processing multiple exceptions
- Corrupted state exceptions (CSE)
- These are essentially low-level Windows API errors
- "Simply put, if the Windows OS sends out a corrupted state error, your program is in very bad shape." (Troelsen: 5th Ed., p. 285)
- If you encounter one of these bad boys you have no choice but to close down the program
- Prior to .NET 4.0
- These errors could be caught using a
System.Exception
catch block - However, there wasn't much you could do to handle them
- These errors could be caught using a
- With .NET 4.0
- New namespace:
System.Runtime.ExceptionServices
(in themscorlib.dll
) - This new namespace is very small - in fact it contains only 2 class types
- Now,
System.Exception
catch blocks will not automatically handle CSEs - To handle CSEs in your catch blocks must apply the
[HandleProcessCorruptedStateExceptions]
attribute to the appropriate method(s) - See Troelsen: 5th Ed., pp. 285-7, for more details
- New namespace:
- Overview: errors, bugs, & exceptions
- Object Lifetime
- Classes, objects, & references
- Using the
new
keyword is what creates an object in memory (i.e., on the heap) - The associated variable name is simply a reference to that object.
- These variables (not the objects themselves) live on the stack
- Using the
- The basics of object lifetime
- General
- By & large you can - and should - let the CLR take care of managing the heap
- CLR has a garbage collector that in normal circumstances will automatically clean unneeded objects from memory
- An object become a candidate for garbage collection once it is unreachable by any part of your code base - i.e., once all references to that object have gone out of scope
- CLR memory management & the CIL of
new
- The CLR maintains a pointer, called the next object pointer or new object pointer
- Identifies where the next object will be placed on the heap
- Pointer is critical in CLR memory management - esp. when compacting/optimizing memory (done on an as-needed basis)
- When C# compiler encounters the
new
keyword it tells the CIL to generate anewobj
instruction - This
newobj
CIL keyword tells CLR to:- Calculate the amount of memory that will be needed to handle the object
- See if sufficient memory exists on the heap to handle the new object.
- If sufficient space does exist:
- The object's constructor is invoked
- The object is placed in memory at the next object pointer
- The calling method is given a reference to the object in memory
- The next object pointer advances to the next available slot on the heap
- If sufficient space does not exist the CLR performs a garbage collection (details below)
- The CLR maintains a pointer, called the next object pointer or new object pointer
- Setting object references to
null
- Compiler generates CIL code to ensure that the reference no longer points to an object in memory
- Specifically, compiler cause CIL to:
- Generate a
ldnull
opcode (this pushes anull
value onto the virtual execution stack) - Generate a
stloc.0
opcode (this sets thenull
reference on the variable)
- Generate a
- Note: none of this, however, forces the CLR to activate the garbage collector!
- General
- Application roots & garbage collection
- "Simply put, a root is a storage location containing a reference to an object on the managed heap." (Troelsen: 5th Ed., p. 294)
- Types of roots:
- References to global objects (though C# does not allow these)
- References to static objects/fields
- References to local objects
- References to object parameters
- References to objects that need to be finalized
- "Any CPU register that references an object" (Troelsen: 5th Ed., p. 294)
- Object graphs
- These are used to document all reachable objects
- Garbage collector will never graph the same object twice (this was apparently a bug in COM programming)
- At garbage collection time CLR will review object graph & mark as garbage any unreachable object
- Components of garbage collection
- Unreachable objects are removed from memory
- Remaining memory is compacted
- Active application roots, & underlying pointers, are updated
- Next object pointer is reset
- Note: there are in fact two heaps
- In addition to the regular one this is another for very large objects
- In practice one can ignore this distinction
- Object generations
- An algorithm used to obviate the CLR having to examine every object during GC
- Each object is assigned to a generation
- Underlying logic: the longer an object has been in memory the more likely it is to be something that will be required throughout the life of the application
- Generations (.NET specifies 3):
- Generation 0: those objects never marked for collection (i.e., new objects )
- Generation 1: objects that have survived one collection (because there was sufficient space on the heap at the time)
- Generation 2: those objects which have survived more than 1 collection
- Note: Generations 0 & 1 are called ephemeral collections
- How a garbage collection handles generations
- GC 1st examines Gen 0 objects , & removes any which are no longer reachable
- If that sweep generates enough free memory the remaining objects are promoted to Gen 1 & further GC stops
- If more memory is still needed the GC moves on to Gen 1, &, if necessary, Gen 2 objects
- Advantage of this GC approach
- New objects are examined quickly when memory is needed
- Conversely, "older objects … are not 'bothered' as often." (Troelsen: 5th Ed., p. 297)
- Garbage collection
- Concurrent garbage collection under .NET 1.0 - 3.5
- Pre-.NET 4.0 the CLR used concurrent garbage collection - all active threads would be suspended whenever the 2 ephemeral generations were being worked through
- This was done to make sure no object was accessing the heap during GC
- If objects in (the non-ephemeral) Gen 2 were being garbage collected that process took place on a dedicated thread
- Purpose of this model was to minimize interruptions to an app during GCs
- Background garbage collection under .NET 4.0
- Note: In reading Troelsen: 5th Ed. (p. 297) it's frankly difficult for me to understand the difference between pre-4.0 GC & 4.0'S background garbage collection.
- Whatever the difference, .NET 4.0 GC is apparently an improvement in efficiency & predictability
- The
System.GC
type- General
- "
System.GC
… allows you to programmatically interact with the garbage collector using a set of static members." (Troelsen: 5th Ed., p. 298) - Rare, if ever, that you will use this - it is essentially to handle internal use of unmanaged resources
- Partial list of members (see Troelsen: 5th Ed., Table 8-1, p. 298, for details):
AddMemoryPressure() & RemoveMemoryPressure()
- must use in tandemCollect()
- force a garbage collectionCollectionCount()
- how many times a generation has been sweptGetGeneration()
- for a single objectGetTotalMemory()
MaxGeneration
(currently = 3)SuppressFinalize()
- see belowWaitForPendingFinalizers()
- typically invoked immediately after callingGC.Collect()
- As of .NET 3.5 SP1:
- Can be notified when a GC is about to occur
- Troelsen believes this feature is of limited use
- "
- Forcing a garbage collection
- Two circumstances where you might want to force a GC:
- You are about to enter a section of code which you do not want interrupted by a GC
- Your app has created a large number of objects and you want to free memory ASAP
- Syntax:
GC.Collect();
GC.WaitForPendingFinalizers(); WaitForPendingFinalizers()
method- Allows finalizable objects the time to finish their clean-up before your app continues
- Suspends calling thread to prevent your code calling methods on objects currently being destroyed!
Collect()
method- Can pass in a parameter specifying the oldest generation to be GC'ed
- Can also pass in a
GCCollectionMode
enumeration (3 values) as a parameter to fine-tune how the GC should operate - As with "regular" GC,
Collect()
promotes surviving generations
- Two circumstances where you might want to force a GC:
- General
- Concurrent garbage collection under .NET 1.0 - 3.5
- Building objects with GC in mind
- Finalizable objects
- The
Finalize()
method- Member of
System.Object
, so it's available to all objects - Default implementation:
public class Object
{
protected virtual void Finalize() { }
} - Notes:
- The method simply provides a location for clean-up code; method is more abstract than virtual
- Method is protected, so it is not available via an instance's dot operator
- Only the GC can call an object's
Finalize()
method
- When
Finalize()
will be called:- During "natural" garbage collection
- You invoke
GC.Collect()
- The application domain hosting your app is unloaded from memory
- The
Finalize()
method & structures- You cannot implement/override
Finalize()
for structures, because, of course, structures do not even live on the heap - If you have a structure which contains an unmanaged resource you use the
IDisposable
interface
- You cannot implement/override
- You never need to override
Finalize()
if making use solely of managed resources - Two ways .NET interacts with unmanaged code:
- By "directly calling into the API of the operating system using Platform Invocation Services (PInvoke)" (Troelsen: 5th Ed., p. 303)
- Complex COM interoperability scenarios (which is typically done via
System.Runtime.InteropServices.Marshall
types) - Ergo, you only need to override
Finalize()
if one of those 2 scenarios exists
- Member of
- "Overriding"
System.Object.Finalize
- You actually do not override finalize
- Instead use (C++-like) destructor syntax
- Syntax:
class MyResourceWrapper
{
˜MyResourceWrapper()
{ //Unmanaged resource clean-up code… }
} - Notes on destructors:
- Never take an access modifier (implicitly
protected
) - No parameters
- Cannot override (i.e., only one destructor is allowed per class)
- Never take an access modifier (implicitly
- Reason for destructor approach is that compiler needs to add to your code
- Never have your destructors refer to managed objects, as you never know whether they might have been destroyed in the GC process
- When you compile a destructor CIL:
- Names/renames the destructor
Finalize()
- Wraps relevant code inside a
try/finally
block
- Names/renames the destructor
- The finalization process
- If an object supports a custom
Finalize
the runtime adds a pointer to the object on an internal "list" called the finalization queue - When GC decides it's time to remove an object from memory it:
- Examines each object on the finalization queue
- Copies each finalizable object off the heap & onto something called the finalization reachable table (a.k.a., the freachable, pronounced "eff-reachable")
- Then the runtime opens a separate thread to invoke
Finalize()
on each object on the freachable - However,
Finalize()
will not be invoked on these objects until the next garbage collection
- All in all, this means finalizable objects will not be truly finalized for at least 2 GCs
- Given all the processing involved, to the extent possibly you really want to avoid including a
Finalize()
method in your classes
- If an object supports a custom
- The
- Disposable objects
- General
- When working with unmanaged resources you may not want to wait for a garbage collection to occur for the resource to be released
- In these cases instead of "overriding"
Finalize()
you implement theIDisposable
interface - Interface:
public interface IDisposable
{
void Dispose()
} Dispose()
& the .NET GC- GC has nothing to do with
Dispose()
- Ergo, structures can implement
IDisposable
- GC has nothing to do with
- Notes:
Dispose()
is invoked by the object userDispose()
method should itself invokeDispose()
on any contained disposable objects- You can rely on an object's
Dispose()
method to invokeDispose()
on contained objects & structures because the container object itself is not being removed from memory by the GC Dispose()
allows an object to clean up resources without all that is involved in the finalization process (i.e., object being placed on freachable, etc.)
- The one "complication" with
Dispose()
- Make sure an object implements
IDisposable
before invoking the method - On the flip side, if an object implements
IDisposable
you should always assume that there is some clean-up code which ought to be run - In other words, always invoke
Dispose()
on objects implementingIDisposable
- A .NET "quirk"
- For some .NET base class libraries it was felt that
Dispose()
was not the most appropriate method name - Ergo, MSFT created duplicate methods for certain .NET base class libraries
- Example:
System.IO.FileStream
has aClose()
method - Where these "alias" methods have been created the underlying code is exactly the same as the code behind
Dispose()
Dispose()
, however, is always available forIDisposable
objects & can always be invoked
- For some .NET base class libraries it was felt that
- Make sure an object implements
- Reusing the C#
using
keyword- "Normally" when using an
IDisposable
object you want to:- Invoke the methods of the object inside a
try
block - Immediately thereafter invoke
Dispose()
inside afinally
block
- Invoke the methods of the object inside a
- In lieu of having to repeatedly create the
try/finally
blocks C# allows a second use for theusing
keyword- Syntax:
using(MyResourceWrapper rw = new MyResourceWrapper())
{ /* Invoke methods on the rw object… */ } - This syntax will insert the requisite
finally { rw.Dispose() }
inside the emitted CIL
- Syntax:
- Can also use this syntax to handle multiple
IDisposable
objects if they are of the same type - Syntax:
using(MyResourceWrapper rw1 = new MyResourceWrapper()
rw2 = new MyResourceWrapper())
{ /* Invoke methods on the rw1 & rw2 objects… */ }
- "Normally" when using an
- General
- Finalizable & disposable objects
- General
- It is possible to create objects that are both finalizable & disposable
- Advantage is that if user forgets to invoke
Dispose()
the GC will ultimately release the unmanaged resources viaFinalize()
- To do this must include
GC.SuppressFinalize(this)
within yourDispose()
code - The
SuppressFinalize()
method tells the GC that the object's destructor does not have to be invoked when the container object is garbage collected
- A formalized disposal pattern
- "Current" drawbacks/concerns:
- Both
Finalize()
&Dispose()
are designed to do the same thing, yet this introduces possibility of duplicate code - You don't want
Finalize()
to remove any managed objects, yet you might wantDispose()
to do precisely that - Want user to be able to invoke
Dispose()
repeatedly without generating errors
- Both
- MSFT recommendation:
- Include a private boolean
disposed
in class - Define a private helper method (typically called
CleanUp()
) to be called by bothFinalize()
&Dispose()
- Have
CleanUp()
take a boolean parameter (typically calleddisposing
) - When calling
CleanUp() Disposing()
passes in a parameter oftrue
&Finalize()
passes infalse
- The booleans help account for all issues discussed
- For full implementation see Troelsen: 5th Ed., pp. 310-2
- Include a private boolean
- Note on disposable objects:
- After running
Dispose()
the object itself may still well be in memory - Therefore, be sure to include traps within appropriate members so that they in effect do nothing after
Dispose()
has been invoked
- After running
- "Current" drawbacks/concerns:
- General
- Finalizable objects
- Lazy object instantiation
- General
- Need/scenario
- In a custom class you have a property which returns an inner type
- That inner type requires a large amount of memory
- "As is" the constructor of your container type should instantiate the inner type whenever the container is new-ed
- However, if the container property corresponding to the inner type is never called the instantiation of that inner type needlessly eats up memory
- One can of course write code to handle this scenario more efficiently
- However, .NET 4.0 introduces a
Lazy<>
(obviously generic) class to handle this situation- Defines data that will not be defined unless app requests it
- Must specify the underlying type that will be created on 1st use
- Syntax:
class ContainerObj
{
//We utilize the default ctor of MyInnerType…
private Lazy<MyInnerType> myType =
new Lazy<MyInnerType>();
//Method which returns the inner type…
public MyInnerType GetTheType()
{
return myType.Value;
}
} - Note use of
Value
property within thereturn
statement
- Need/scenario
- Customizing the creation of lazy data
- You can quickly envision scenarios where the above syntax is overly simplistic:
- Might want to employ a ctor other than the default one
- Might have other, related code to run upon instantiating the inner type
- Solution:
- Can specify a generic delegate as an optional parameter with
Lazy<>
class - That delegate specifies a method to call when you create the wrapped type
- Delegate is of type
System.Func<>
- Delegate can point to a method which returns the same data type as the wrapped type
- That method can take up to 16 arguments
- Those arguments are themselves generic type parameters
- Troelsen observations (see Troelsen: 5th Ed., p. 316):
- Will rarely need to specify any such parameters
- Recommends using a lambda expression to handle the delegate
- See Troelsen: 5th Ed., p. 316, for sample code
- Delegate is of type
- Can specify a generic delegate as an optional parameter with
- You can quickly envision scenarios where the above syntax is overly simplistic:
- General
- Classes, objects, & references
- General
- Advanced C# Programming Constructs
- Covariance & contravariance (aka, variance)
- General
- Closely related to casting
- Assignment compatability
- Essentially another phrase for implicit casting
- Example:
string str = "test";
object obj = str;
- Covariance is a feature which allows for implicit reference conversion when dealing with parent/child types
- "Covariance preserves assignment compatibility and contravariance reverses it." (msdn.microsoft.com)
- Covariance & contravariance available with
- Array types
- Generic type arguments
- Delegate types
- Arrays
- "Covariance for arrays enables implicit conversion of an array of a more derived type to an array of a less derived type." (msdn.microsoft.com)
- Example:
object[] array = new String[10];
- Note: this construct is not type safe, so you will get a run-time error if you then implement the following line of code:
array[0] = 10;
- General
- Interfaces
- Interface basics
- General
- "An interface is nothing more than a named set of abstract members." (Troelsen: 5th Ed., p. 321)
- As abstract, there are no default implementations
- "An interface expresses a behavior that a given class or structure may choose to support." (Troelsen: 5th Ed., p. 321)
- A class or structure may support multiple interfaces
- Note: .NET convention is to begin all interfaces with the letter
I
- .NET base class libraries, of course, have plenty of built-in interfaces
- Interface types vs. abstract base classes
- Abstract (base) classes can contain:
- Abstract members (to provide a polymorphic interface)
- Constructors
- Field data
- Nonabstract members (with implementation)
- Interfaces, however, can only contain abstract member definitions
- Polymorphic support
- With abstract classes you are limited to that class's derived types
- Interfaces, however, can be applied across class hierarchies (i.e., they are highly polymorphic)
- Another limitation of abstract classes:
- Every derived class must provide an implementation of the base class's abstract members
- With interfaces you can cherry-pick the classes which must support the abstract methods you want implemented
- Abstract (base) classes can contain:
- General
- Custom interfaces
- Defining custom interfaces
- Use
interface
keyword - Interfaces never specify a base class (though you can derive from another interface)
- Never specify an access modifier for interface members - they are implicitly
public & abstract
- Never provide implementation details
- Other than methods, interfaces can specify:
- Property prototypes (including whether read-only or write-only)
- Event definitions
- Indexer definitions
- You cannot "new" an interface
- Example:
// This will throw a compiler error…
IMyInterface mi = new IMyInterface(); - Example (explicit casting can be done with interfaces, though):
// Often need to do this if class has employed
// "explicit interface implementation" (see below)…
IMyInterface mi = (IMyInterface)someObject;
- Example:
- Use
- Implementing an interface
- Listed after the colon operator in the type definition
- Interfaces are listed after any base-class listing
- Listings are comma-delimited
- When implementing an interface a type must implement all members of that interface
- Listed after the colon operator in the type definition
- Working with interfaces
- Invoking interface members at the object level
- No differences from invoking regular members
- Options when you are unsure of whether an object implements an interface
- "Naïve" approach
- Attempt an explicit cast
- Wrap that cast in a
try
block catch
forInvalidCastException
- Use the
as
keyword- Example:
SomeClass sc = new SomeClass();
ISomeInterface Itemp = sc as ISomeInterface;
if (ITemp != null)
{ // further code… } - What's nice about this approach is that it obviates the need for a
try/catch
block
- Example:
- Obtaining interface references: the
is
keyword- Use an
if
statement - Example:
SomeClass sc = new SomeClass();
if (sc is ISomeInterface)
{ // further code… } - Again, obviates need for a
try/catch
block
- Use an
- "Naïve" approach
- Interfaces as parameters - can be done, just as with any other type or datatype
- Interfaces as return values - can be done
- Arrays of interface types
- Can be constructed
- These tend to be very powerful programming constructs as they allow you to deal with objects across various class hierarchies
- Invoking interface members at the object level
- Implementing interfaces using VS 2010
- When you declare a class implementing an interface:
- The letter
I
of the interface name will be underlined in VS - The underline is a smart tag
- Clicking on the smart tag gives you a pop-up box
- After making either choice in the pop-up box the interface will be stubbed out for you in the class
- The letter
-
menu
- There is an option in VS's menu
- Pulls a new interface definition out of existing code
- When you declare a class implementing an interface:
- Resolving name clashes via explicit interface implementation
- When implementing multiple interfaces in a class or structure there exists the possibility that the interfaces contain identical members
- When you encounter name clashes you need to use explicit interface implementation
- Example: You have a
Draw()
method withinIDrawToPrinter
&IDrawToForm
interfaces - Syntax (to distinguish between implementations):
class SomeClass : IDrawToForm, IDrawToPrinter
{
void IDrawToForm.Draw() { /* Implementation... */ }
void IDrawToPrinter.Draw() { /* Implementation... */ }
} - Notes:
- You do not supply an access modifier with explicit interface implementations
- These implementations are implicitly
private
- To access the methods (which, as
private
, now cannot be accessed via dot operator) you must explicitly cast your object
- "Second" use of explicit interface implementation
- Allows you to hide members that you don't want visible from normal dot notation
- However, a more sophisticated user of your object can gain access to the method via an explicit cast
- Designing interface hierarchies
- General
- These work just like class hierarchies
- You do not re-declare the methods from the base interface inside the derived interface
- A class or structure which implements a derived interface will have to implement all of the methods up the interface inheritance chain
- Multiple inheritance with interface types
- Perfectly ok
- Wrinkle with name-clashing methods:
- You have 1 interface which inherits from another 2 interfaces
- Those 2 interfaces have identically named methods
- You define a class or structure which implements the 1 child interface
- How does that class or structure implement the name-clashing method?
- Two possible implementations:
- 1) The class or structure can simply implement 1 version of the name-clashing methods
- 2) The class or structure can use explicit interface implementation so that 2 versions are available
- Note: Under Option 2 the object or structure would have to be explicitly cast to access the appropriate name-clashing method
- General
- Defining custom interfaces
- Essential .NET base class libraries interfaces
- Building enumerable types (
IEnumerable
&IEnumerator
)- General
- Both
IEnumerable & IEnumerator
are found in theSystems.Collection
namespace - Using
foreach
keyword/construct- Typically used to iterate through an array
- However,
foreach
can handle any type which supports aGetEnumerator()
method
- Relationship between
IEnumerable
&IEnumerator
GetEnumerator()
is the one & only method identified byIEnumerable
GetEnumerator()
has a return type, & that type isIEnumerator
- Definition:
public Interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
} - Implementing the two interfaces
- "Theory":
- You can develop customized implementations of
MoveNext()
, etc. - You can also "manually" get a hold of
IEnumerator
& work with that - Syntax/Example -
Widgets (IEnumerable)
"collection" class:
IEnumerator i = Widgets.GetEnumerator();
i.MoveNext();
Widget w = (Widget)i.Current;
- You can develop customized implementations of
- Practice: use the functionality built into
System.Array
- Assume you have a "container" class
- Essentially, you turn this class into a wrapper around an
array
- Then send the
GetEnumerator()
request into an embedded/private array - Syntax/Example:
//Container class for Widget objects…
public class MyWidgets : IEnumerable
{
private Widget[] wdgtArr = new Widget[4];
//Ctor…
public MyWidgets()
{ /* populate wdgtArr with new widgets */ }
//Now implement IEnumerable…
public IEnumerator GetEnumerator()
{
//Simply invoke method on array…
return wdgtArr.GetEnumerator();
}
} - Note: If you want to "hide" the
GetEnumerator()
at the object level use an explicit interface implementation
- "Theory":
- Both
- Building iterator methods with the
yield
keyword- An alternative to using
foreach
construct is to use iterators - Note:
yield
keyword is, in fact, never used alone - usage is alwaysyield return
- Iterators allow you to control how items are returned within a
foreach
construct - Implementation specifics:
- Your "container class" does not implement
IEnumerable
! - Iterator method must still be named
GetEnumerator()
- Return type must still be
IEnumerator
- Your "container class" does not implement
- Syntax Option 1 (using
foreach()
construct):
public class Widgets
{
private Widget[] myWdgts =
new Widget[]{ /*populate array… */ };
public IEnumerator GetEnumerator()
{
foreach(Widget w in myWdgts)
{ yield return w }
}
} - Syntax Option 2 (sans
foreach()
construct):
public class Widgets
{
private Widget[] myWdgts =
new Widget[]{ /*populate array… */ };
public IEnumerator GetEnumerator()
{
yield return myWdgts[0]
yield return myWdgts[1]
//etc…
}
} - Notes:
- In either case after code reaches
yield return
the CLR keeps track of the location within the array for the next call intoGetEnumerator()
(via "hidden" CIL code) - Option 2 obviously becomes unwieldy with an array of any size
- In either case after code reaches
- An alternative to using
- Building a named iterator
yield
(i.e.,yield return
) keyword(s) can be used inside any method- In other words, you are not constrained to the
GetEnumerator()
method - If you create another method for
yield/yield return
that method must still return an interface - However, you return an
IEnumerable
type, not anIEnumerator
return type - Syntax/example:
// Inside, say, a WidgetsCollection class…
public IEnumerable GetWidgets(bool ReverseOrder)
{
if ReverseOrder
{
for (int i = wdgtArray.Length; i !=0; i--)
{ yield return wdgtArray[i-1]; }
}
else
{
foreach (Widget w in wdgtArray)
{ yield return w; }
}
}
- In other words, you are not constrained to the
- Advantages of creating a named iterator:
- Once you move away from
GetEnumerator()
you can pass parameters into your iterator - The reason for a parameterized named iterator is that you can create multiple (i.e., customized) iterators
- Examples: methods which return items in reverse order, which return every 3rd item, etc.
- See Troelsen: 5th Ed., pp. 347-8, for code examples
- Once you move away from
- Calling a named iterator
- That a named iterator returns
IEnumerator
rather thanIEnumerable
of course affects how you call a named iterator - Syntax/Example:
foreach(Widget w in Widgets.YourNamedIteratorMethod(parameters…))
- That a named iterator returns
- Internal representation of an iterator method
- Compiler "generates a nested class definition within the scope of the defining type" (Troelsen: 5th Ed., p. 348)
- That auto-generated nested class in turn implements
GetEnumerator()
MoveNext()
Current
property- (But not
Reset()
!)
- General
- Building cloneable objects (
ICloneable
)- Basic
- Can always get a shallow copy via
MemberwiseClone()
method ofSystem.Object
- Method is protected, though, so object users can't call it
MemberwiseClone()
is useful during cloning processICloneable
specifies a single method,Clone()
- Note:
- Troelsen (Troelsen: 5th Ed., p. 350) points out that there is nothing in the
ICloneable
specification requiring thatClone()
return a deep copy of an object - Troelsen also notes that this has created debate & confusion in the .NET community
- Troelsen (Troelsen: 5th Ed., p. 350) points out that there is nothing in the
- Basic approach behind
Clone()
:- Create a new, internal instance of the class
- Update the copy's properties
- Return that object to the
Clone()
invoker - Note: when setting the new object's property remember to use
this
keyword to get containing object's properties
- Streamlined approach: If (and only if) your type has no internal reference type variables simply invoke
MemberwiseClone()
- Can always get a shallow copy via
- Advanced (i.e., when your type contains reference-type variables)
- Typical implementation (of
Clone()
method):- Create a internal shallow copy of the class using
MemberwiseClone()
- Create any new instances of internal types
- Set the properties of those internal types (e.g.,
newInternalType.Ppty = this.InternalType.Ppty
) - Return the updated copy of this type
- Create a internal shallow copy of the class using
- Again, if your type has no internal reference types simply rely on
MemberwiseClone()
- Typical implementation (of
- Basic
- Building comparable objects (
IComparable
&IComparer
)IComparable
- Definition:
public Interface IComparable
{
int CompareTo(object o);
} - Typical implementation (comparing on PptyX):
int IComparable.CompareTo(object obj)
{
MyObj temp = obj as MyObj;
if (temp != null)
{
if (this.PptyX > temp.PptyX)
return 1;
if (this.PptyX < temp.PptyX)
return -1;
else
return 0;
}
else
throw new ArgumentException("Wrong parameter type…");
}
} - Streamlined implementation (if your property is, say, an
int
):
int IComparable.CompareTo(object obj)
{
MyObj temp = obj as MyObj;
if (temp != null)
return this.PptyX.CompareTo(temp.PptyX)
else
throw new ArgumentException("Wrong parameter type…");
}
- Definition:
- Specifying multiple sort orders
IComparer
IComparer
is inSystems.Collections
namespace- Definition:
Interface IComparer
{
int Compare(object o1, object o2);
} - You generally do not implement on the class type you are interested in comparing
- Instead, you implement this on helper/utility classes, building one for each sort order (i.e., the property on which you want to run comparisons)
- Typically want to name your helper class
…Comparer
- Example:
public class WidgetNameComparer : IComparer
int IComparer.Compare(object o1, object o2)
{
Widget w1 = o1 as Widget;
Widget w2 = o2 as Widget;
if(w1 != null && w2 != null)
return String.Compare(w1.ItemName, w2.ItemName);
else
throw new ArgumentException("Parameter not a Widget!");
}
} - Using
System.Array
System.Array
has several overloadedSort()
methods- One such
Sort()
method takes an object implementingIComparer
- Specifically, you pass in your collection object and your
…Comparer
object - See Troelsen: 5th Ed., p. 358, for an example of how to implement
- Custom properties, custom sort types
- Can create a custom, static property to help user sort your objects
- Example:
public class Widget : IComparer
{
…
// Read-only property returning …Comparer class…
public static IComparer SortByItemName
{ get { return (IComparer)new WidgetNameComparer(); } }
}
- Can now use:
Array.Sort(myWidgets, Widget1.SortByItemName);
- Building enumerable types (
- Interface basics
- Generics
- General
- Most rudimentary .NET object container is
System.Array
class- Used to define a set of identically-typed items
- Fixed upper limit
- Often, however, need ability to:
- Grow & shrink container
- Create more elaborate screening of types of contained objects
- One step up from
System.Array
isSystem.Collections
Systems.Collections.Generics
appeared with release of .NET 2.0- Constraints are an important facet of generics
- Most rudimentary .NET object container is
- The issues with non-generic collections
- General
System.Collection
classes, & the interfaces they implementArrayList
- dynamically sized collection of objects, in sequential orderIList
ICollection
IEnumerable
ICloneable
Hashtable
- collection of key/value pairs, organized on the hash code of the keyIDictionary
ICollection
IEnumerable
ICloneable
Queue
- standard, FIFO queueICollection
IEnumerable
ICloneable
SortedList
- likeHashtable
, but sorted by key value & accessible via key value or indexIDictionary
ICollection
IEnumerable
ICloneable
Stack
- LIFO collection, with push, pop, & peek capabilitiesICollection
IEnumerable
ICloneable
- Interfaces used in non-generic .NET collections
ICollection
- defines general characteristics of a collection, e.g. size, enumeration, & thread safetyICloneable
- see Building cloneable objectsIDictionary
- requires that a collection represent its contents via key/value pairsIEnumerable
- see Building enumerable types (IEnumerable
&IEnumerator
)IEnumerator
- see Building enumerable types (IEnumerable
&IEnumerator
)IList
- specifies behavior required to add, remove, & index items in a sequential list of objects
Systems.Collections.Specialized
namespace- Contains (obviously) non-standard collection types, such as:
ListDictionary
StringDictionary
StringCollection
- Contains other interfaces & abstract base classes
- Contains (obviously) non-standard collection types, such as:
- 'Last word' on
System.Collections
&Systems.Collections.Specialized
namespace classes- There are issues (discussed below) with:
- Performance
- Type safety
- Any project created with .NET 2.0 or later should ignore these classes and instead use the collection classes in
System.Collections.Generic
namespace
- There are issues (discussed below) with:
- Performance
- Boxing
- "Boxing can be formally defined as the process of explicitly assigning a value type to a
System.Object
variable." (Troelsen: 5th Ed., p. 364) - Example:
…
int myInt = 25;
object myBoxedInt = myInt;
… - CLR process
- Creates new object on the heap
- Copies value into that object
- Creates pointer to the object
- "Boxing can be formally defined as the process of explicitly assigning a value type to a
- Unboxing
- "Unboxing is the process of converting the value held in the object reference back into a corresponding value type on the stack." (Troelsen: 5th Ed., p. 364)
- Operative line of code would be:
int unboxedInt = (int)boxedInt;
- CLR process
- First verifies that receiving data type is the same as the boxed type
- Copies value into that stack-based variable
- Challenges
- When unboxing you must do so into an appropriate variable type or you will raise an
InvalidCastException
- To handle this possibility you would typically wrap the unboxing code within a try/catch block
- When unboxing you must do so into an appropriate variable type or you will raise an
- Significance of boxing/unboxing with non-generic collections
- Non-generic collection classes operate on objects
- Even when creating, say, an
ArrayList
of integers behind the scenes the CLR must box each integer before passing it into theArrayList
- When retrieving those integers from the
ArrayList
would therefore need to cast each item back asint
variables - When working with a lot of data, all of the CLR steps required to box/unbox can slow down your app
- Boxing
- Type safety
- Because non-generic collection classes take objects, those collections can wind up holding anything & everything in the .NET world
- Further, there is no constraint that the 'items' within a given such collection are even of the same type (other than being of
System.Object
) - Prior to generics the method for ensuring type-safety:
- Create a custom collection class, e.g.
WidgetCollection
- Implement
IEnumerable
interface with class - Create a private, say,
ArrayList
inside theWidgetCollection
class - Create custom public methods, e.g.,
AddWidget(Widget w)
, which was nothing more than a wrapper method for, say,arWidgets.Add(w)
- Create a custom collection class, e.g.
- While this approach ensured type safety it had a big drawback
- You needed to create a custom class for every collection of every type of object
- Even though the underlying code & logic was essentially the same, you needed to create, say,
WidgetCollection, EmployeeCollection, ToolCollection
, etc.
- Further, you were still incurring boxing/unboxing penalties for collections of value-type items!
- Generic collections
- Solve the above problems (including no boxing/unboxing)
- Largely eliminate need to build custom collection classes
- Guarantee type safety
- General
- Generic type parameters
- General
- Classes, structures, interfaces & delegates can be written generically
- Enums, however, cannot
- Nomenclature of generics
- The
T
in, say, theList<T>
class is referred to as a:- Token
- Type parameter
- "Placeholder"
- Read/pronounce
<T>
as "of T" - Read/pronounce
List<T>
as:- "List of T"
- "List of type T"
- Read/pronounce
List<Widget> myWidgets = new List<Widget>();
as:- "A List<> of T, where T is of type Widget"
- "A List of Widget objects"
- The
- Conventions
- Use
T
to represent types - Use
TKey
orK
to represent keys - Use
TValue
orV
to represent values
- Use
- Specifying type parameters for generic classes / structures
- "Once you specify the type of parameter of a generic item,[sic] it cannot be changed…" (Troelsen: 5th Ed., p. 373)
- "When you specify a type parameter for a generic class or structure,[sic] all occurrences of the placeholder(s) are now replaced with your supplied value." (Troelsen: 5th Ed., p. 373)
- If you look at, say,
List<T>
, in in VS you find the placeholderT
throughout the type definition - When you define, say,
List<Widget>
, effectively theT
placeholder is replaced withWidget
- Specifying type parameters for generic members
- Non-generic classes & structures can contain & support generic members
- Must specify placeholder value at time you invoke the method / set property
- Specifying type parameters for generic interfaces
- There are many generic counterparts to non-generic interfaces - e.g.,
IComparable<T>
- Huge advantage of generic interfaces is that you then do not have to implement any casting operations, runtime checks, etc. on object type
- There are many generic counterparts to non-generic interfaces - e.g.,
- General
- The
System.Collections.Generics
namespace- General
- Namespace is defined in
mscorlib.dll
&System.dll
assemblies - The namespace contains replacements for most of the non-generic collection classes
- Counter-intuitively, many generic collection classes extend their non-generic counterparts (this allows the generic counterparts to work with legacy code)
- Namespace interfaces
- List
ICollection<T>
IComparer<T>
IDictionary<TKey, TValue>
IEnumerable<T>
IEnumerator<T>
IList<T>
ISet<T>
- allows for the abstraction of sets
- Covariance in generic interfaces
- The
T
,TKey
, &TElement
are covariant in the following generic interfaces:IEnumerator<T>
IEnumerable<T>
IQueryable<T>
IGrouping<TKey, TElement>
- Example:
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
- The
- Contravariance in generic interfaces
- The
T
is contravariant in the following generic interfaces:IComparer<T>
IEqualityComparer<T>
IComparable<T>
- Example
- You have a base & a derived class
- You then create a BaseClassComparer class, implementing the
IEqualityComparer<BaseClass>
interface - Because
IEqualityComparer<T>
is contravariant you can use BaseClassComparer to run comparisons on derived classes - Syntax/example:
class BaseClass { }
class DerivedClass : BaseClass { }
// Comparer class…
class BaseComparer : IEqualityComparer<BaseClass>
{
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void InvokeContravariance()
{
IEqualityComparer<BaseClass> baseComparer =
new BaseComparer();
IEqualityComparer<DerivedClass> childComparer =
baseComparer;
}
}
- The
- Limitations on variance in generic interfaces:
- Only works for reference types (i.e., not value types)
- In other words, you cannot implicitly convert
IEnumerable<int>
toIEnumerable<obj>
, becauseint
is a value type - Also, "classes that implement variant interfaces are still invariant." (msdn.microsoft.com)
- For example,
List<T>
implements the covariant interfaceIEnumerable<T>
- Ergo, you cannot implicitly convert
List<Object>
toList<String>
- (You could, however, implicitly convert as follows:
IEnumerable<Object> listObjects = new List<String>();
)
- For example,
- List
System.Collections.Generics
classes & the interfaces they implementDictionary<TKey, TValue>
- generic collection of keys & valuesICollection<T>
IDictionary<TKey, TValue>
IEnumerable<T>
List<T>
- dynamically resizable sequential listICollection<T>
IEnumerable<T>
IList<T>
LinkedList<T>
- a doubly-linked listICollection<T>
IEnumerable<T>
- Note: see Wikipedia for good discussions of:
Queue<T>
ICollection
- i.e., non-generic interfaceIEnumerable<T>
SortedDictionary<TKey, TValue >
- generic collection of sorted keys & valuesICollection<T>
IDictionary<TKey, TValue>
IEnumerable<T>
SortedSet<T>
- ensures no duplicationICollection<T>
IEnumerable<T>
ISet<T>
Stack<T>
ICollection
- i.e., non-generic interfaceIEnumerable<T>
- Notes
System.Collections.Generics
defines many other classes & structures- Many of these work in conjunction with above classes
- Example:
LinkedListNode<T>
is a node withinLinkedList<T>
- Libraries other than
mscorlib.dll
&System.dll
add types to the namespace
- Namespace is defined in
- Collection initialization syntax
- Introduced in .NET 3.5
- Very much akin to Object initializer syntax
- Syntax very similar to that used to initialize arrays
- Restriction
- Can only use this approach with classes that support an
Add()
method Add()
collection is specified by theICollection/ICollection<T>
interfaces
- Can only use this approach with classes that support an
- Syntax (simple data type):
List<int> myListNos = new List<int> { 0, 1, 2, 3 };
- Syntax (blending object & collection syntax):
List<Musician> listBeatles = new List<Musician>
{
new Musician { LastNm = "Lennon", FirstNm = "John" },
new Musician { LastNm = "McCartney", FirstNm = "Paul" },
//…
};
- The
List<T>
class- The most commonly used generic collection
Insert()
method allows you to specify where inList<T>
you want to add a new item- There is also a
ToArray()
method:
Musicians[] Beatles = listBeatles.ToArray();
- Other methods also exist
- The
Stack<T>
class- Use
Push()
to add an item - Use
Pop()
to remove an item - If
Stack<T>
is empty:Pop()
will throw anInvalidOperationException
- A smart programmer places
Pop
commands within aTry/Catch
block
- Use
- The
Queue<T>
classDequeue()
methodEnqueue()
methodPeek()
method
- The
SortedSet<T>
class- Introduced in .NET 4.0
- Automatically sorts items as they are added or removed
- Obviously you need to tell compiler, though, how you want items sorted
- Need to pass in an object that implements
IComparer<T>
interface - To do this:
- Create a class that implements
IComparer<T>
interface - Specify the object type you want to sort by in this declaration
- Define the class as having just one method:
public int Compare(…)
- Utilize your comparer class when new-ing the collection
- Syntax/example:
// Comparer class…
class SortPeopleByAge : IComparer<Person>
{
public int Compare(Person firstPerson, Person secondPerson)
{
if (firstPerson.Age > secondPerson.Age)
return 1;
if (firstPerson.Age < secondPerson.Age)
return -1;
else
return 0;
}
}
// Create sorted set, including your comparer class as a
// constructor argument when new-ing your person class…
SortedSet<Person> setOfBluthes =
new SortedSet<Person>(new SortPeopleByAge())
{
new Person { FirstName="George", LastName="Bluthe", Age=65 },
new Person { FirstName="Michael", LastName="Bluthe", Age=40 },
};
// Add more people…
setOfBluthes.Add(new Person { FirstName = "George Michael",
LastName = "Bluthe", Age = 14 });
setOfBluthes.Add(new Person { FirstName = "Maebe",
LastName = "Fünke", Age = 14 });
- Create a class that implements
- Need to pass in an object that implements
- General
- Creating custom generic methods
- General
- Generally, base class libraries work just fine
- Custom generics have effect of creating super overloading
- Example: Without generics if you need, say,
Swap()
methods, you need to create essentially identical code for each type you might want to swap - Could somewhat alleviate this by defining a method that takes an
object
parameter, but that gets back to all issues which drove creation of generics in first place (casting, type safety, etc.) - Look to create custom generics "Whenever you have a group of overloaded methods that only differ by incoming arguments…" (Troelsen: 5th Ed., p. 386)
- Syntax of custom generic methods is to insert a type parameter between the method name & its parameter list
- Additionally, when creating custom generic methods you can employ multiple parameters - e.g.:
static void Swap<T>(ref T a, ref T b)
{ // implementation code… }
- Inference of type parameters
- If you have a generic method that takes parameters you have the option of omitting the type declaration
- Compiler will infer the correct type for you
- Example (using full declaration syntax):
Swap<int>(ref T a, ref T b);
- Example (using full declaration syntax):
int a = 1, b = 2;
Swap<int>(ref a, ref b); - Example (using type inference):
int a = 1, b = 2;
Swap(ref a, ref b); - Troelsen (5th Ed., p. 387), however, suggests leaning on type inference can lead to tears
- General
- Creating custom generic structures & classes
- General
- Syntax:
Public Class MyClass<T>
- Syntax for structure is the same
- Can then use generic type parameter in constructors, properties, methods, etc.
- See Troelsen: 5th Ed., p. 389, for sample code
- Syntax:
- The
default
keyword in generic code- May want to (re)set a field to the default property of the generic type
- Syntax:
_field1 = default(T);
- The
default
keyword, of course, is also used inswitch
statements - Defaults are:
- Numeric values:
0
- Reference types:
null
- Fields of a structure:
0
for value types &null
for reference types
- Numeric values:
- Generic base classes
- Generic classes can be base classes to other classes
- Ergo, generic classes can define virtual & abstract methods
- Restrictions
- Derived class must specify the type parameter of the base class
- Example:
Public Class DerivedClass : BaseClass<string>
- If the base class contains generic virtual or abstract methods the derived class must use the specified type parameter when overriding those methods
- If derived based class is also generic it:
- May reuse the type placeholder in its definition
- Must honor all constraints on the base class
- General
- Constraining type parameters
- General
- While generics are type-safe, you can further tighten up your code by using the
where
keyword - You can use more than one
where
constraint - Options
where T : struct
- i.e., the type parameter must haveSystem.ValueType
in its lineagewhere T : class
- The type parameter must not have
System.ValueType
in its lineage - In other words, it must be a reference type
- The type parameter must not have
where T : new()
- The type parameter must have default constructor
- Helpful if your generic type must create an instance of type parameter (after all, can never know custom constructors)
- If using 1+
where
clauses this must be listed last
where T : NameOfBaseClase
- i.e., the type parameter must haveNameOfBaseClass
in its lineagewhere T : NameOfInterface
- i.e., the type parameter must implement the specified interface(s)
- While generics are type-safe, you can further tighten up your code by using the
- Using the
where
keywordwhere T : new()
& compile-time error checking- Default constructor is removed whenever you create a custom constructor
- Will find if programmer remembered to recreate default constructor
- The
where
keyword specifies which parameter is being constrained (as you could also haveTKey
orTValue
parameters - Example of using multiple constraints:
public class MyClass<T> where T : class, IDrawable, new()
- Example (multiple constraints on multiple parameters):
public class MyClass<T, K> where T : struct, IComparable<T>
where K : SomeBaseClass, new()
{ // Implementation code… } - You can also use
where
keyword on generic methods - Derived classes
- Derived types must honor constraints
- Example:
public class MyBaseClass<T> where T : new()
{ //Implementation code… }
public class MyDerivedClass<T> : MyBaseClass<T> where T : new()
{ //Implementation code… }
- Lack of operator constraints
- You cannot apply any C# operators (e.g.,
+, -, *
, etc.) on type parameters out-of-the-box - This is true even if you have constrained your parameter to be a
struct
- If you really need to use operators on generic parameters you could do so via interfaces, as interfaces can define operators!
- You cannot apply any C# operators (e.g.,
- General
- General
- Delegates, Events & Lambdas
- General
- "A delegate is a type that safely encapsulates a method. " MSDN > Library > Development Tools and Languages > Visual Studio 2015 > C# Programming Guide > Delegates > Using Delegates
- "Delegates are used to pass methods as arguments to other methods." MSDN > Library > Development Tools and Languages > Visual Studio 2015 > C# Programming Guide > Delegates
- Delegate types
- Type-safe objects
- "Point to" a method (or list of methods) that can be invoked later
- Built-in support for:
- Multicasting
- Asynchronous method invocation
- Delegates provide a way to abstract a method, similarly to the way interfaces provide abstractions of classes
- Delegates
- Basics & definitions
- The .NET
Delegate
type- Unlike traditional C-style callbacks (created via function pointers), .NET delegates are configured to specify:
- Number & types of parameters
- Return type (if any) of pointed-to method
- With the encapsulation & abstractionprovided by delegates (of methods) they are very similar to interfaces' representation of classes
- Can be set up to invoke/call its target method(s) based on conditions at runtime
- Can point to either static or instance methods
- Two ways of invoking methods
- Synchronously
- Asynchronously - & can do so without having to deal with
Thread
object
- Unlike traditional C-style callbacks (created via function pointers), .NET delegates are configured to specify:
- Common uses for delegates
- As dicussed below in Sending object state notifications using delegates, delegates are often used to get information about the state of an object
- Callbacks
- Sometimes in code you need an object that can "call you back" - i.e., you send the object a message & the object sends a message back to you
- Most frequently see callback mechanisms with graphical user interfaces (e.g., buttons)
- "Under the .NET platform, the delegate type is the preferred means of defining and responding to callbacks within applications." (Troelsen: 5th Ed., p. 397)
- Notifying a caller when a long process has completed (i.e., asynchonous callbacks)
- Passing one method into another
- This can be very powerful
- Syntax/example:
class Program
{
// Define delegate…
delegate void MyDel(string message);
static void Main(string[] args)
{
MyDel handler = HandleStringMsg;
// Invoke method, passing in MyDel instance…
UsingMyDel(1, 2, handler);
}
static void HandleStringMsg(string message)
{ Console.WriteLine(message); }
static void UsingMyDel(int x, int y, MyDel callback)
{
callback(string.Format("Sum of {0} + {1} =
{2}", x, y, (x + y)));
}
}
- Defining a custom comparison method
- Encapsulate the method in a delegate
- Then pass that method into a sorting algorithm
- Defining a
Delegate
type in C#- Utilize
delegate
keyword- Even though
Delegate
is one of the 5 main .NET types, you do not- Create a stand-along file for it, as you do with classes & interfaces
- Even employ scope brackets to define a delegate
- Example (note simplicity):
public delegate int BinaryOp(int x, int y);
- Must match signature of method will point to
- This example can point to any method which returns an
int
& takes 2ints
as parameters
- Even though
- How C# creates a delegate class 'behind the scenes'
- Creates a sealed class deriving from
System.MulticastDelegate
(which in turn derives fromSystem.Delegate
) - Note: though delegates inherit from
System.MulticastDelegate
(&, therefore, in turn, fromSystem.Delegate
) you will receive a compiler error if you try to invoke this inheritance explicitly - 3 methods in class:
Invoke()
- Calls method synchronously - i.e., called method must finish before calling method can continue
- Though you may explicitly call
Invoke()
there is no need to do so (seen below)
BeginInvoke()
&EndInvoke()
- Both commands are used for asynchronous method invocation - i.e., on separate thread of execution
- Again, no need for you to work with
Thread
object
- How compiler handles the 3 methods
Invoke()
- signature exactly matches the signature of the delegateBeginInvoke()
- Return type:
IAsyncResult
- 1st parameters: parameters - & all parameter modifiers - of the delegate
- Additional parameters:
AsyncCallback
type, &object
type to capture state
- Return type:
EndInvoke()
- Return type: the return type, if any, of the delegate
- Parameter(s): an
IAsyncResult
type
- Example:
sealed class BinaryOp : System.MuticastDelegate
{
public int Invoke(int x, int y);
public IAsyncResult BeginInvoke(int x, int y,
AsyncCallback cb, object state);
public int EndInvoke(IAsyncResult result);
}
out, ref,
¶ms
parameters- These can all be handled
Invoke()
&BeginInvoke()
methods operate just as described aboveEndInvoke()
, however, changes- Any & all
out
&ref
parameters of delegate are now included (along with theIAsyncResult
type) - Return type remains the type returned by the delegate
- Any & all
- Creates a sealed class deriving from
- Utilize
- Delegates, pointed-to methods, & objects
- While a delegate points to a method, if the method is an instance method the delegate also 'has knowledge' of the instance
- That said, all the delegate 'knows' about the
object
is that it contains a method whose signature matches that of the delegate - As such, the delegate is otherwise agnostic about the method-containing object
- "When a delegate is constructed to wrap a static method,[sic] it only references the method." MSDN > Library > Development Tools and Languages > Visual Studio 2015 > C# Programming Guide > Delegates > Using Delegates
- The
System.MulticastDelegate
&System.Delegate
base classesSystem.MulticastDelegate
methods, operators, & fields (partial list)- A delegate inherits from this only when it points to multiple methods
public sealed override Delegate[] GetInvocationList()
- returns list of methods 'pointed to'- Overloaded operators
public static bool operator ==(MuticastDelegate d1, MulticastDelegate d2);
public static bool operator !=(MuticastDelegate d1, MulticastDelegate d2);
- Internal fields
private IntPtr _invocationCount;
private object _invocationList;
- Notes on
IntPtr
type- Typically used to represent a pointer or handle
- Implements
ISerializable
- Designed to be an integer
- Size is platform specific - i.e., 32-bits on 32-bit hardware, 64-bits on 64-bit hardware
- See MSDN Library > .NET Development > .NET Framework 4.6 and 4.5 > .NET Framework Class Library > System > IntPtr Sturcture for full details
System.Delegate
methods, operators, & properties (partial list)- Note:
System.Delegate
class implementsICloneable, ISerializable
interfaces - Methods (use to handle list of functions):
public static Delegate Combine(params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);
public static Delegate Remove(Delegate source, Delegate value);
public static Delegate RemoveAll(Delegate source, Delegate value);
- Overloaded operators
public static bool operator ==(Delegate d1, Delegate d2);
public static bool operator !=(Delegate d1, Delegate d2);
- Properties that return the delegate target:
public MethodInfo Method { get; }
public object Target { get; }
- Note:
- Core members:
Method
property- Returns a
System.Reflection.MethodInfo
object - Gives you info about static method maintained by delegate
- Returns a
Target
property- "…returns an object that represents the method maintained by the delegate." (Troelsen: 5th Ed., p. 401)
- If this property returns
null
, however, the method to be called is a static member
Combine()
- Adds a method to the delegate list
- In C# invoke via the (overloaded)
+=
operator
GetInvocationList()
- Returns an array of
System.Delegate
objects - Each such object represents a method which you can invoke
- Returns an array of
Remove()
&RemoveAll()
- Static methods
- In C# can use overloaded
-=
operator in lieu ofRemove()
- Instantiating & using a
Delegate
- Must include a/the pointed-to method when instantiating a delegate
- Syntax/example:
class Program
{
static void Main(string[] args)
{
// Create delegate type…
public delegate int BinaryOp(int x, int y);
// Instantiate delegate…
BinaryOp addInts = AddTwoInts;
// Alternate instantiation syntax…
BinaryOp addInts = new BinaryOp(AddTwoInts);
}
// Pointed-to method…
static int AddTwoInts(int x, int y)
{ return x + y; }
} - (Discussed below is another option, C# anonymous methods)
- After declaring a delegate instance & having it 'point to' a method you simply use the name of the delegate variable in lieu of the name of the pointed-to method
- Example (using the above BinaryOp example):
b(1,2);
, whereb
is aBinaryOp
instance (obviously pointing to a method which takes 2ints
& returns anint
) - You also have the option of explicitly calling
Invoke()
- e.g..,b.Invoke(1,2);
(takes 2ints
& returns anint
)
- Multicasting
- Again, this is where a delegate calls > 1 method when invoked
- Used extensively in event handling
- Methods will be called in the order in which they were attached to the delegate instance
- Reference parameters
- Passed from one method to next
- A change to the value of a referenced parameter is visible to ensuing methods
- Unhandled exceptions
- Passed to the delegate caller
- No further methods are invoked
- Caller will receive the return value and/or out parameter of the last method invoked
- Reference parameters
- Syntax/example:
class Program
{
// Define delegate…
delegate void MyDel(string message);
static void Main(string[] args)
{
// Define delegate, class variables…
MyDel handler = HandleStringMsg;
SomeClass obj = new SomeClass();
// Define delegate variables…
MyDel d1 = obj.StrHandler1;
MyDel d2 = obj.StrHandler2;
MyDel d3 = HandleStringMsg;
// Enable multicasting…
MyDel multiDel = d1 + d2;
multiDel += d3;
multiDel -= d1; // …remove a method
// Get fancy…
MyDel oneMethodDel = multiDel - d2;
}
static void HandleStringMsg(string message)
{ Console.WriteLine(message); }
}
class SomeClass
{
internal void StrHandler1(string message) { }
internal void StrHandler2(string message) { }
}
- The .NET
- Sending object state notifications using delegates
- General
- Notifying callers of object state is a big reason for using delegates
- To do this you typically:
- Define a public delegate type inside the object of interest
- This will be used to point to methods outside the object - i.e., to send state notifications
- Effectively an event handler
- Declare a (private) member variable of this delegate type
- This will hold list of methods to be invoked under various circumstances
- Downside of
private
scope: must create a register-with-delegate method (i.e., rather than simply allowing caller to attach methods) - Upside of
private
scope: better adherence to encapsulation (& type safety)
- Create a (public) helper function in the object that allows a caller to attach/register an external method to/with the delegate object
- Function should take the delegate defined within the class as its sole parameter
- This is why the delegate type must be declared as public
- To do this the argument should be
(new yourObjInstance.PublicDelegateProperty(nameOfExternalMethod))
- Remember to check that private variable
!= null
before attaching a function to it!
- Syntax/example:
class MyClass
{
// PUBLIC ppty of delegate type…
public delegate void MyHandler(string msgForCaller);
// Private delegate instance …
private MyHandler listOfHandlers;
// Registration function for callers…
public void RegisterWithMyHandler(
MyHandler methodToCall)
{
if (listOfHandlers == null)
{
listOfHandlers = new MyHandler(methodToCall);
}
else
{
listOfHandlers += methodToCall;
}
}
} - Syntax/example (registering a method with your object):
class Program
{
static void Main(string[] args)
{
// New instance of your class…
MyClass mc = new MyClass();
// Register the method to call…
mc.RegisterWithMyHandler(
new myClass.MyHandler(MethodToCall));
}
// Define the event-capturing method…
public static void MethodToCall(string msg)
{ // Implementation code… }
}
- Define a public delegate type inside the object of interest
- See Troelsen: 5th Ed., pp. 405-8, for full sample code
- Enabling multicasting
- To add additional methods to a delegate continue to make use of
+=
operator - Can alternatively make use of static
Delegate.Combine()
method
- To add additional methods to a delegate continue to make use of
- Removing targets form a delegate's invocation list
- There is a static
Delegate.Remove()
method - As a shortcut can make use of
-=
operator - Typically you create a public helper method for removing a method, just as you create one for adding a method
- There is a static
- General
- Method group conversion syntax
- Comes into play regarding the event-registration method you create in a custom object
- Background:
- You create a (public) property of your
delegate
type - You then pass a
new
(external) instance of that delegate type into your registration method
- You create a (public) property of your
- Problem:
- You have created a delegate object simply for the purpose of telling your object's event-handler which method to point to
- You then have an object (i.e., the delegate created in your object-calling code) 'kicking around' after its one-time use
- Solution (i.e., method group conversion syntax):
- In point of fact, you really are only interested in supplying the name of the method(s) to be attached (or detached)
- In cases where a method takes a delegate as parameter(s) method group conversion syntax means you need only supply a (pointed-to) method name
- Syntax/example:
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
// Register with simple method name…
mc.RegisterWithMyClass(SendMsgHere);
}
static void SendMsgHere(string msg)
{ // Handle string msg… }
}
- Delegate covariance
- Relates to cases where a delegate's pointed-to method returns a custom class type
- Specifically:
- Assume you have 2 custom class types:
class Parent {//Implementation code… }
class Child : Parent {//Implementation code… }
- You have two methods:
public static ObjParent MethodP();
public static ObjChild MethodC();
- Assume you have 2 custom class types:
- Prior to .NET 2.0 if you wanted a delegate which pointed to
MethodC
it had to be a different delegate from one pointing toMethodP
- Delegate Covariance (a.k.a. relaxed delegates), however, allows you to create one delegate pointing to both methods in our example
- Approach
- Create a delegate type which points to method returning the parent object
- Create an instance of this delegate, but pass in a method which returns the child type
- Lastly, when utilizing the delegate instance cast the returned object to the child type
- Example/Syntax (creating a delegate instance which points to child object):
// Create delegate type…
delegate ObjParent ObtainParentTypeDelegate();
// Create a method returning a child type…
static ObjChild GetChildType()
{ return new ObjChild(); }
// Use covariance to return ObjChild…
ObtainParentTypeDelegate handlerParent = GetChildType;
// Cast the result…
ObjChild child = (ObjChild)handlerParent(); - Notes:
- Troelsen's sample code shows this final cast (Troelsen: 5th Ed., p. 415)
- MSDN, however, indicates a final cast is not necessary (see MSDN > Library > Development Tools and Languages > Visual Studio 2015 > C# And Visual Basic Shared Programming Concepts > Covariance and Contravariance > Variance in Delegates)
- Delegate contravariance
- Contravariance allows you to create one delegate pointing to multiple methods where the methods receive objects related by classical inheritance
- Syntax/example:
// Event hander that accepts a parameter of the EventArgs type…
private void MultiHandler(object sender, System.EventArgs e)
{ label1.Text = System.DateTime.Now.ToString(); }
public Form1()
{
InitializeComponent();
// You can use a method that has an EventArgs parameter,
// although the event expects the KeyEventArgs parameter…
this.button1.KeyDown += this.MultiHandler;
// You can use the same method
// for an event that expects the MouseEventArgs parameter…
this.button1.MouseClick += this.MultiHandler;
}
- Note: Example is from MSDN Library > Development Toos and Languages > Visual Studio 2015 > C# and Visual Basic Shared Programming Concepts > Covariance and Contravariance > Varance in Delegates > Using Variance in Delegates (C# and Visual Basic), at msdn.microsoft.com
- Generic delegates
- General/Example
- You have 2 methods which each return
void
& take a single parameter - The parameter for one method is a
string
, & for the 2nd it is anint
- You can declare a single (generic) delegate type to handle both methods
- Your instances of that (generic) delegate type are then configured appropriately
- Example:
//Class-level delegate type…
public delegate void MyGenericDel<T>(T arg);
//Create delegate instance to handle a 'string' method…
MyGenericDel<string> strDel =
new MyGenericDel<string>(StringMethod);
//Create delegate instance to handle an 'int' method…
MyGenericDel<int> intDel =
new MyGenericDel<int>(IntMethod);
- You have 2 methods which each return
- Simulating generic delegates without generics
- Prior to .NET 2.0 you achieved the 'same' result by defining a delegate which took an
object
parameter - Example:
public delegate void MyNonGenericDel(object arg);
- Pointed-to method would then (of course) have to take an
object
as its parameter - Further, guts of this method would have to:
- Test the
arg
parameter to see its type - Cast the object to the appropriate type
- Use an
if
orswitch
statement to branch to the appropriate operating code
- Test the
- As was the case with much pre-.NET 2.0 code, the drawbacks were:
- Lack of type safety
- Boxing/unboxing penalties
- Prior to .NET 2.0 you achieved the 'same' result by defining a delegate which took an
- General/Example
- Basics & definitions
- C# events
- Limitations with delegates
- Fair amount of boilerplate code
- Creating/defining the delegate
- Creating a private instance of the delegate
- Creating a registration method
- Risk: failure to declare delegate member variables as
private
- Obviously caller would then have access to the delegate variable
- Caller could remove pointed-to methods from a delegate variable
- Caller could also directly invoke pointed-to methods
- See Troelsen: 5th Ed., pp. 418-9, for example of using
Invoke()
method of a delegate instance to send one's string message to an external method
- Fair amount of boilerplate code
- The C#
event
keyword- Basically, it just provides some syntactical shortcuts
- Compiler automatically creates registration & unregistration methods
- Also establishes private delegate member variables
- 2 basic steps in creating
- Declare the appropriate (public) delegate type
- This delegate will, of course, hold the list of methods to be fired by the event
- Declare (public)
events
corresponding to the appropriate delegate type - Syntax/example:
public class MyClass
{
// Declare delegate to work with events…
public delegate void MyClassSimpleEventHandler(string msg);
// See "Creating custom event arguments"
// below for this syntax…
public delegate void MyClassFullEventHandler(
object sender, MyClassEventArgs e);
// Declare events…
public event MyClassSimpleEventHandler Event1;
public event MyClassFullEventHandler Event2;
}
- To utilize - i.e., to send an
event
to a caller- Specify the event by name at the appropriate place in code - obviously supplying the parameters dictated by the delegate declaration
- Note: before using an event remember to test the event instance against a
null
value (this confirms caller has registered a method with the event) - Syntax/example (adapted from car example in Troelsen: 5th Ed., p. 420):
class Car
{
// Field & ppty declarations omitted…
// CarEventArgs defined as a separate class, see below…
public delegate void CarEngineHandler(
object sender, CarEventArgs e);
// Declare events…
public event CarEngineHandler Exploded;
public event CarEngineHandler AboutToBlow;
public void Accelerate(int delta)
{
if (carIsDead) //…boolean field
{
if (Exploded != null)
Exploded(this, new CarEventArgs("Car is dead…"));
}
else
{
CurrentSpeed += delta;
// Too fast…
if (10 == (MaxSpeed - CurrentSpeed) &&
AboutToBlow != null)
{
AboutToBlow(this, new CarEventArgs("Slow down!"));
}
// Handle other cases…
}
}
}
- Basically, it just provides some syntactical shortcuts
- Events under the hood
- When creating an event CIL adds 2 hidden methods
- The 2 methods are named by adding
add_
&remove_
prefixes to the name of the event (e.g.,add_myEvent, remove_myEvent
) - These methods obviously take the place of the registration methods you would create by hand if not using events
- Note:
add_
method makes call to delegate'sCombine()
methodremove_
method makes call to delegate'sRemove()
method
- CIL also makes use of
.addon
&.removeon
directives
- Listening to incoming events
- Last piece is handling how you register caller-side event handlers with events
- Instead of creating handler-registration methods you simply utilize
+=
&-=
operators - Syntax (verbose):
YourObject.YourEvent += new RelatedDelegate(YourMethod);
- Syntax (using method group conversion syntax):
YourObject.YourEvent += YourMethod;
- Event registration using VS 2010
- IntelliSense window pops up whenever you type
+=
syntax to register an event - Use key to complete code
- IntelliSense window pops up whenever you type
- Creating custom event arguments
- MSFT has a recommended event pattern, which is seen in base class library events
- Specifically, MSFT recommends that your event handlers be built with at least 2 parameters (in order):
System.Object
- to pass along a reference to the object firing the event- The
System.Object
reference is usually calledsender
- Pass in the
this
keyword to establish the reference
- The
- A descendent of
System.EventArgs
(usually callede
)- Used to include information about the event
- You
new
an instance of this class within the event-firing code
System.EventArgs
base class:
public class EventArgs
{
public static readonly System.EventArgs Empty;
public EventArgs();
}- "For simple events, you can pass an instance of
EventArgs
directly." (Troelsen: 5th Ed., p. 425) - For more complex events you:
- Create a public class inheriting from
EventArgs
- Use an instance of that class as the 2nd argument in your external event handler
- Syntax/Example:
public class MyClassEventArgs : EventArgs
{
public readonly string msg;
public MyClassEventArgs(string message) // …ctor
{ msg = message; }
}
- Create a public class inheriting from
- With the event handler:
- Cast the received
object
(i.e.,sender
) to the appropriate class type - Best Practice: wrap the cast inside a
if (sender is YourClass)
trap - Note, however: Event handlers do not have to include the
sender
&e
types as parameters! - Event handlers also have the option of including only 1 of the 2 types as parameters
- This is obviously an exception to the requirement that delegates have signatures which match their pointed-to functions
- Cast the received
- See Troelsen: 5th Ed., pp. 424-6, for a full example
- The generic
EventHandler<T>
delegate- If you are not worried about sending an instance of your object as part of your delegate you can streamline matters even further
- Specifically, instead of creating a class inheriting from
System.EventArgs
you can use generics! - Example 1:
public event EventHandler<YourEventArgs> MethodToInvoke;
- This eliminates the need to even define a custom delegate type inside your custom class
- In calling code you then use
EventHandler<YourEventArgs>
in lieu of the delegate type
- Limitations with delegates
- C# anonymous methods
- General
- As things stand event-handling code is still a little verbose
- Typically, the event-handling code is not called by other areas of your project
- However, you have still built a 'full-blown' method for that very limited purpose
- To handle this wee bit of inefficiency .NET provides anonymous methods
- Immediately following your event-registration line include, in braces, your event-handling code
- Move the semicolon from the end of the event-registration line & place it after the code-closing brace
- Anonymous methods therefore eliminate the need to create (static) event-handling methods
- Generic syntax
class Program
{
static void Main(string[] args)
{
MyType t = new MyType();
// NO semi-colon!…
t.SomeEvent += delegate(optionallySpecifiedDelegateArgs)
// INCLUDE semi-colon…
{ /* event-handling code */ };
}
}
- As things stand event-handling code is still a little verbose
- Accessing local variables
- Anonymous methods are unusual in that they are able to access the local variables of their defining methods
- These referred-to variables are called outer variables
- Rules/characteristics for anonymous methods & variables
- Cannot access outer
ref
orout
variables - Can access instance & static outer variables
- Cannot have its own local variable with same name as an outer local variable
- Can declare local variables with same name as outer class member variables
- Note: in this last circumstance the anonymous method's local variable has its own scope & hides the outer class variable
- Cannot access outer
- General
- Lambda expressions
- General
- "Lambda expressions are nothing more than a very concise way to author anonymous methods…" (Troelsen: 5th Ed., p. 430)
- Syntax/example:
delegate int myDel(int i);
static void Main(string[] args)
{
myDel md = i => i * i;
int j = md(5); // … j == 25
}
- The
FindAll()
method ofList<T>
- Definition:
public List<T> FindAll(Predicate<T> match)
- What makes this method particularly interesting is that
Predicate<T>
is a .NET delegate type - Definition:
public delegate bool Predicate<T>(T obj);
Predicate<>
is used to point to a method which pulls out a subset- When you use
FindAll()
each item inList<T>
is passed to the method to whichPredicate<T>
points - When the pointed-to method returns
true
theFindAll()
method adds the passed-in object to theList<T>
- Definition:
- The
FindAll()
method ofList<T>
is frequently used with lambda expressions - Even using anonymous methods
- You eliminate having to create a
Predicate<T>
type explicitly - On the other hand you must either:
- Use the
delegate
keyword, or - Create a strongly-typed
Predicate<T>
- Use the
- You eliminate having to create a
- Lambdas represent significantly more compact code
- Can be used anywhere you would use
- Anonymous methods
- A strongly-typed delegate
- Anonymous Method example:
List<int> evenNumbers = list.FindAll(delegate (int i)
{
return (i % 2) == 0;
}); - Lambda example:
List<T> evenNumbers = list.FindAll(i => (i % 2) == 0);
- Can be used anywhere you would use
- See Troelsen: 5th Ed., pp. 430 - 3, for full example & discussion
- Dissecting a lambda expression
- List of parameters precede the
=>
token- These parameters can be implicitly or explicitly typed
- When explicitly typing parameters put the variable definition statement inside parentheses
- Example:
… list.FindAll((int i) => (i % 2) == 0);
- Statements which will process the parameters follow the
=>
token - If desired, it is perfectly acceptable to wrap the entire lambda calculus in parentheses
- List of parameters precede the
- Processing arguments within multiple statements
- You can build lambda expressions that contain multiple statement blocks
- Predictably, you wrap multiple statements inside scope brackets
- Lambda expressions with multiple (or zero) parameters
- Simply place comma-delimited list of parameters inside parentheses & in front of
=>
token - For a delegate which takes no parameters indicate that by using empty parentheses
- Simply place comma-delimited list of parameters inside parentheses & in front of
- General
- General
- Misc. Advanced C# Language Features
- Indexer methods
- General
- When defining custom classes & structures can include an indexer method, which allows you to have your class or structure behave just like an array
- You do this by:
- Having your custom collection implement
IEnumerable
- Including a private
ArrayList
, which is the guts of the collection - Adding a special property definition, which makes use of
this[]
syntax, to return from & add to the internalArrayList
- Note: because we are using the non-generic
ArrayList
the propertyget-er
has to cast the object returned from theArrayList
- Having your custom collection implement
- Definition syntax:
public class WidgetCollection : IEnumerable
{
private ArrayList arWidgets = new ArrayList();
// Now add custom indexer…
public Widget this[int index]
{
get { return (Widget)arWidgets[index]; }
set { arWidgets.Insert(index, value); }
}
} - Usage syntax:
WidgetCollection myWidgets = new WidgetCollection();
// Adding objects…
myWidgets[0] = new Widget( /* set ctor ppts… */ );
myWidgets[1] = new Widget( /* set ctor ppts… */ );
// etc… - Why use custom indexers?
- Technically you can accomplish all this with custom
AddWidget()
&GetWidget()
methods - However, using indexer methods makes your class more compatible with other .NET types
- All in all, more often than not your best approach is to use generic collections
- Technically you can accomplish all this with custom
- Indexing data using string values
- Use
System.Generic.Dictionary<Tkey, Tvalue>
rather than anArrayList
inside your custom collection class - Definition syntax:
public class WidgetCollection : IEnumerable
{
private Dictionary<string, Widget> dictWdgts =
new Dictionary<string, Widget>();
// Custom indexer based on serial number…
public Widget this[string serialNo]
{
get { return (Widget)dictWdgts[serialNo]; }
set { dictWdgts[serialNo] = value); }
}
// optionally add method for ClearAll(), property for Count…
} - Usage syntax:
WidgetCollection myWidgets = new WidgetCollection();
// Adding objects…
myWidgets["ABC123"] = new Widget( /* set ctor ppts… */ );
myWidgets["XYZ456"] = new Widget( /* set ctor ppts… */ ); - Again, more often then not you are better off simply using generics than creating a custom collection class
- Use
- Overloading indexer methods
- Can be done
- For example, you might want to be able to access items in a collection by either an index value or a string
- Many .NET types have overloaded indexer methods
- Probably most significant example is ADO.NET's
DataTableCollection
typeDataSet
type sports aTables
property, which returns aDataTableCollection
DataTableCollection
has 3 indices:- Ordinal position (passing in an
int
) - A
string
moniker string
moniker plus namespace (string
)
- Ordinal position (passing in an
- Indexers with multiple dimensions
- You would need to do this if your underlying container is a multi-dimensional array
- This is used by ADO.NET's
DataTable
type, where you pass in a row & column number - When constructing your own you again use the
this
keyword
- Indexer definitions on interface types
- Can define an interface so that any type implementing the interface must implement custom indexing
- Example (for an indexer obtaining Widget objects via an int):
public interface IWidgetContainer
{
Widget this[int index] { get; set; }
}
- General
- Operator overloading
- General
- C# operators which can be overloaded
- Unary:
+, -, !, ~, ++, --, true, false
- Binary:
+, -, *, /, %, &, |, ^, <<, >>
- Comparison operators:
==, !=, <, >, <=, >=
- Note: if you overload one comparison operator you must also overload its logical pair - e.g., if overloading
<
you must also overload>
- Unary:
- C# operators which cannot be overloaded
[]
- though, as just discussed, you can create custom indexers()
- though, as discussed below, you can create custom conversion methods- Shorthand assignment operators:
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
- However, once you overload the related binary operator the related assignment operators overload 'free'
- C# operators which can be overloaded
- Overloading binary operators
- To override any operator C# provides the
operator
keyword operator
keyword can only be used with static methods- "When you overload a binary operator (such as
+
and-
), you will most often pass in two arguments that are the same type as the defining class…" (Troelsen: 5th Ed., p. 446)- However, one of your parameters can be of a different type
- Example:
- You have a type
Square
& you overload the*
operator to take aSquare
& anint
- You would likely be writing the code-behind to take the dimensions of the
Square
type & multiply them by theint
to give you a newSquare
- You have a type
- Note: the return type of the operator will also be the same type as the defining class
- An overloaded operator is really no different from any other static method
- To override any operator C# provides the
- The
+=
&-+
operators- These operators are automatically simulated whenever you overload the related binary operator
- Ergo, there is no need - nor ability! - to write overloading code for them
- Overloading unary operators (i.e., increment & decrement operators)
- As unary, you can only pass in a single parameter to operators like
++
&--
- You again employ the
operator
keyword & again must declare your overloaded operator asstatic
- Also, your one parameter must be of the defining class (or structure)
- Syntax/example (
Point
structure):
public struct Point
{
// …
// Add 1 to the X, Y values of the incoming Point…
public static Point operator ++(Point p)
{ return new Point(p.X+1, p.Y+1); }
// Similar code for decrementing…
} - Note re: pre- & post- incrementing & decrementing:
- C++ allows you to overload pre- & post-decrementing separately
- Cannot do this with C#
- However, once you define, say,
++pt
(as above)pt++
is defined automatically for you
- As unary, you can only pass in a single parameter to operators like
- Overloading equality operators
- Default for
System.Object.Equals()
is a reference-based comparison - Can override to perform a value-based comparison
- Note: if you override
System.Object.Equals()
you typically also want to overrideSystem.Object.GetHashCode()
- Once you override
System.Object.Equals()
it is easy to override==
&!=
operators - Syntax/example:
public class Point
{
// …
public override bool Equals(object o)
{ return o.ToString() == this.ToString(); }
public override int GetHashCode()
{ return this.ToString().GetHashCode(); }
// Override == operator…
public static bool operator ==(Point p1, Point p2)
{ return p1.Equals(p2); }
// Overriding != operator is similar…
}
- Default for
- Overloading comparison operators
- To overload these your class of course needs to implement the
IComparable
interface - Just as overloading the equality operators was accomplished by internal references to
System.Object.Equals()
implementation, to overload comparison operators you refer toCompareTo()
implementation - Example:
public class Point : IComparable
{
//…
public int CompareTo(object obj)
{ // Implementation code… }
public static bool operator < (Point p1, Point p2)
{ return (p1.CompareTo(p2) << 0); }
// etc.…
}
- To overload these your class of course needs to implement the
- Internal representation of overloaded operators
- Represented in CIL as hidden methods like
op_Addition, op_Subtraction
, etc. - Each hidden method takes the
specialname
CIL token/decoration - See Troelsen: 5th Ed., Table 12-2, p. 452, for full list operator-to-CIL method name mapping
- Represented in CIL as hidden methods like
- Summary
- Overloading operators is generally useful only when you are building utility types
- Many .NET types have overloaded operators
- General
- Custom type conversions
- General/review
- Base datatypes
- You can always store a smaller datatype "in" a variable of a larger datatype without any special coding (implicit casting)
- To go the other way you must make an explicit cast
- Reference types related by inheritance work similarly
- You can always store a derived type as instance of one of its base types
- To go the other way you must make an explicit cast
- Converting between types that are not related by inheritance
- Probably not something which will occur often
- However, you could conceivably want to, say, cast a
Square
as aRectangle
, & visa versa! - Could create custom methods to do this
- However, C also allows you to create methods which employ the
()
casting operator
- Base datatypes
- Creating custom conversion routines
- In addition to
operator
keyword you also make use of eitherexplicit
orimplicit
keyword - Syntax/example (to convert a
Rectangle
to aSquare
type:
public class Square
{
// ctors, methods, etc.…
public static explicit operator Square(Rectangle r)
{ // implementation code… }
} - Inside a
Square
class you could also define a method which casts, say, from aSquare
to aSystem.Int32
- Syntax/Example:
// other code…
public static explicit operator int (Square s)
{ return s.Length; } - All in all, then, using
explicit
keyword you can write code which will convert either from or to the custom type
- In addition to
- Defining implicit conversion routines
- Two catches
- "…it is illegal to define explicitly and implicit conversion functions on the same type if they do not differ by their return type or parameter set." (Troelsen: 5th Ed., p. 459)
- "…when a type defines an implicit conversion routine,[sic] it is legal for the caller to make use of the explicit cast syntax!" (Troelsen: 5th Ed., p. 459)
- Syntax/Example:
public class Rectangle
{
// other code…
// Cast Square to Rectangle by doubling
// the length of the square to get Rect width…
public static implicit operator Rectangle (Square s)
{
Rectangle r = new Rectangle();
r.Height = s.Length;
r.Width = s.Length * 2;
return r;
}
}
- Two catches
- Internal representation of custom conversion routines
op_explicit
keywordop_implicit
keyword
- General/review
- Extension methods
- General
- Introduced with .NET 3.5
- Allows you to add functionality to already-compiled types:
- Classes
- Structures
- Interfaces
- Can also add functionality to types being compiled within your own project
- Restrictions
- Must be defined within a static class (ergo, each extension method must be defined as
static
) - Must use the
this
keyword as modifier on the 1st (& only on the 1st) parameter - Extension methods may be called on either:
- An instance in memory
- Statically, via the defining class
- Must be defined within a static class (ergo, each extension method must be defined as
- Syntax/example:
static class MyExtensions
{
static MyExtensions() { }
// This method allows any integer to reverse its digits…
public static int ReverseDigits(this int i)
{
// Convert int into an array of chars, then reverse them…
char[] digits = i.ToString().ToCharArray();
Array.Reverse(digits);
// Put the chars back into a string…
string newDigits = new string(digits);
// Return the modified string back as an int…
return int.Parse(newDigits);
}
}
- Defining & invoking extension methods
- The parameter to which you apply the
this
keyword/modifier is the type to which you are adding extension methods - You can add other parameters (again, though, only one
this
-modified parameter is allowed) - This, in turn, means you can overload extension methods!
- Significance of
static
&this
keywordsthis
keyword means extension methods can be (& usually are) invoked on an instance of the appropriate type- However, because extension methods are static you can also call them as such
- Syntax (for calling extension methods statically):
MyNamespace.MyExtMethodUtilClass.MyExtMethod(appropriateTypeInstance);
- The parameter to which you apply the
- Extension method scope
- Extension methods cannot access non-public members of the type they apply to
- Extension does not equal inheritance!
- In practice
- Extension methods cannot access a type's fields
- Extension methods can access values returned by public properties of an instance of the class
- Importing types that define extension methods
- Be careful about working with types defining extension methods where the type is defined in a namespace other than your project's 'native' namespace
- In these cases you have to import the appropriate namespace (via
using
keyword) - Extension methods are limited to namespaces that
- Define them
- Import them
- The Intellisense of extension methods
- Extension methods do appear in IntelliSense
- Marked with a blue down-arrow icon
- Building & using extension libraries
- Create a class library in VS
- If you are going to export your methods remember to flag the defining type/class as
public
(default access modifier for a type isinternal
) - You will of course have to import the appropriate namespace name (via
using
keyword) in the client project - MSFT recommendations re: types with extension methods:
- Place these types within a dedicated assembly
- Use a dedicated namespace for these types
- This reduces clutter
- Extending interface types via extension methods
- Approach differs somewhat from that used with classes & structures
- "When you extend an interface with new members, you must also supply an implementation of those members!" (Troelsen: 5th Ed., p. 469)
- Supplying an implementation seems to contradict exactly what it is to be an interface
- However, you cannot declare a type of the interface & then invoke the extension method
- The extension method appears only on types implementing the interface
- Remember to take care to use namespaces correctly in all this
- General
- Partial methods
- General
partial
keyword can be used with methods (as well as with classes)- Partial methods appears in part to be an attempt to allow you to mimic C++ constructs
- In C++ you have header & implementation files
- With C#
partial
methods you:- Prototype your method in one file
- Implement the method in another files
- Requirements/restrictions on partial methods
- Can only be defined within partial classes
- Must return
void
- Can be either static or instance-level methods
- Can have arguments & with one exception (
out
modifier) can have access modifiers - Are implicitly private
- Lastly, note that partial methods are not necessarily emitted into the compiled assembly!
- Writing partial methods
- Set up
- You put the signature of a partial method in one partial class file (you omit scope/implementation braces)
- You put the full definition of the partial method in the other partial class file
- Flag both sets of code with
partial
keyword
- If implementation is not provided
- You will see no trace of the method's definition in the compiled assembly
- You will also see no trace of invocations of the method!
- Partial methods are C#'s version of conditional code compilation
- 'Standard' preprocessor directives are:
#if, #elif, #else
, &#endif
- However, partial methods are strongly typed!
- 'Standard' preprocessor directives are:
- Set up
- Uses of partial methods
- In practice the use of partial methods restricted by the requirements that they:
- Return
void
- Are implicitly private
- Return
- Where they can be useful
- Someone building part of a
partial
class would have to write elaborate implementation code for a method - Partial methods are a superior option versus:
- Using preprocessor directives
- Supplying dummy implementation to virtual methods
- Throwing
NotImplementedException
objects
- Someone building part of a
- Lightweight events
- Most common use of partial methods
- Enables class designers to provide method hooks which co-developers can implement or not
- Naming convention is to use
On
as a naming prefix for such methods
- In practice the use of partial methods restricted by the requirements that they:
- General
- Anonymous types
- General
- Essentially an extension of anonymous methods concept
- For use where you need a class to
- Model encapsulated/related data
- You need no methods, events, etc.
- Work only within current app
- In other words, this is where you need a 'temporary' class
- To create you use:
var
keyword- Object initialization syntax
- Syntax/example:
var homeTown = new { City = "New Providence",
emsp;State = "NJ", ZipCode = "07974" }; - The variable you define with
var
gets implicitly typed
- Internal representation of anonymous types
- Anonymous types derive from
System.Object
, &, as such, support the usual object methods:ToString()
GetHashCode()
Equals()
GetType()
- If you get the
GetType().Name
property on an anonymous type you will get something like:<>f_AnonymousType0`3
- From object initialization syntax compiler generates:
private readonly
fields- Identically named
readonly
properties
- Anonymous types derive from
- The implementation of
ToString()
&GetHashCode()
- The
ToString()
method of an anonymous object will essentially yield your object initialization syntax (including braces) - The
GetHashCode()
method- Uses property names & values as inputs
- Ergo, 2 anonymous types will give you the same hash code if & when they have identically-named properties & values for those properties
- The
- The semantics of equality for anonymous types
System.Object.Equals()
- Uses value-based semantics when operating on anonymous types
- Therefore, 2 anonymous types will show as equal if defined with the same property/value pairs
==
&!=
operators- These are not overridden by the compiler when creating anonymous types
- Ergo, these operators are reference- (rather than value-) based
- Note re: underlying type of anonymous types
- Two anonymous types with identical property name/value pairs will have identically named underlying types
- Compiler only generates a new underlying type if there is a new property name or value
- Anonymous types containing anonymous types
- Can be done
- Syntax/example:
var purchaseInfo = new {
TimeBought = DateTime.Now,
ItemBought = new {#123; Color = "Red",
SKU="123abc", Descr="Widget" },
Price = 19.99 };
- Summary
- You typically use anonymous types only with LINQ
- Limitations to anonymous types
- You cannot name them
- They always extend
System.Object
- Their fields & properties are read-only
- They cannot support
- Events
- Custom methods
- Custom operators
- Custom overrides
- There is no inheritance (anonymous types are implicitly sealed)
- They lock you in to their default constructor
- Useful in LINQ when you (again) want simply to model the shape of an object
- General
- Pointer types
- General
- Two major types of .NET data:
- Value types
- Reference types
- Pointer types represent a 3rd type
- Using pointer types, for better & for worse, means that you hare handling memory management yourself
- C# pointer-related operators & keywords (many/most recognizable from C++)
*
- Creates a pointer variable - a variable that represents a direct location in memory
- Also used for pointer indirection
&
- gives you the address of a variable in memory->
- Used to access fields of a type represented by a pointer
- Function is same as that of C# dot operator (though unsafe!)
[]
- Allows you to index the slot pointed to by a pointer variable++
,--
- Used to increment/decrement pointer types+
,-
- Standard addition/subtraction on pointer types==
,!=
,<
, etc. - Standard equality & comparison operatorsstackalloc
- Can only be used in an unsafe context
- Allows you to place arrays directly on the stack
fixed
- Can only be used in an unsafe context
- Temporarily fixes a variable, allowing you to find variable's address
- Caveats
- If you start manipulating pointers CLR essentially says "You're on your own now, buddy…"
- Will rarely, if ever, have to use pointer types in .NET
- The 2 cases where you might conceivably user pointer types
- "You are looking to optimize select parts of your application by directly manipulating memory outside the management of the CLR." (Troelsen: 5th Ed., p. 480)
- You are making a call to a C-based
.dll
or to a COM server, & the method demands a pointer type for one or more of its parameters - Note: in the 2nd case you are typically better off using:
System.IntPtr
type- Members of the
System.Runtime.InteropServices.Marshall
type
- Setting up a project where you will use pointer types
- Must inform compiler that you will employing unsafe code
- If working from the command line include the
/unsafe
flag as an argument (i.e.,csc /unsafe *.cs
) - In VS check box on project's Property page
- Two major types of .NET data:
- The
unsafe
keyword- Any & all pointer-related code must be written within the scope of the
unsafe
keyword - All other code is considered safe by default
- As necessary,
unsafe
keyword can be used to flag classes, structures, type members, & parameters - Any calls to unsafe methods must themselves be placed within an
unsafe
scope
- Any & all pointer-related code must be written within the scope of the
- Working with the
*
and&
operators- In C & C++ the
*
operator is applied to the variable names - In C# the
*
operator is applied to the type identifier - Syntax/examples:
// C and C++…
int *aa, *bb;
// C#…
int* x, y; - Other syntax examples:
unsafe static void SomeMethod()
{
int myInt;
// Assign an int pointer the address of myInt…
int* ptrToMyInt = &myInt;
// Use pointer indirection to assign value…
*ptrToMyInt = 456; }
- In C & C++ the
- Using the
ref
keyword as an alternative to using pointers- Troelsen (Troelsen: 5th Ed., pp. 483-4) has great examples of an unsafe & a safe Swap function
- Unsafe example:
unsafe public static void UnsafeSwap(int* i, int* j)
{
int temp = *i;
*i = *j;
*j = temp;
} - Safe example:
public static void SafeSwap(ref int i, ref int j)
{
int temp = i;
i = j;
j = i;
}
- Field access via pointers (the
->
operator)- Assume you have defined a
Point
structure, withint
propertiesX, Y
- Field access syntax:
unsafe static void FieldPointing()
{
Point point;
Point* p = &point;
p-> = 123;
Console.WriteLine(p->ToString());
} - Note: Once you use the deallocation operator (
*
) you can revert to dot notation! - Pointer indirection syntax:
unsafe static void UsingPointerIndirection()
{
Point point;
Point* p = &point;
(*p).x = 789;
}
- Assume you have defined a
- The
stackalloc
keyword- When working with pointers you sometimes need a local variable that allocates memory from the call stack
- Note: once you do this that memory is out of reach of .NET garbage collection
- Syntax/example:
unsafe static void UnsafeStackAlloc()
{
char* p = stackalloc char[256];
for (int k = 0; k < 256; k++)
p[k] = (char)k;
}
- Pinning a type via the
fixed
keyword- When using the
stackalloc
keyword memory is cleaned up as soon as thestackalloc
method is done - This, after all, is the nature of memory on the stack
- However, things get more complicated with reference types
- Reference types, of course, exists on the garbage-collected heap
- This means (under normal circumstances)
- The item can be garbage collected at any moment
- The item can also be moved in memory, as the GC process optimizes memory
- You run into obvious problems, then, when you have pointers pointing to reference types
fixed
keyword obviates these issues by pinning a variable in memory during code execution- Note: Any time that you reference a managed variable from
unsafe
code C# compiler will require use of thefixed
keyword - See Troelsen: 5th Ed., pp. 485-6, for sample code
- When using the
- The
sizeof
keyword- Gives you the size (in bytes) of a value type
- Cannot be used with reference types
- Syntax/example:
unsafe static void SizeOfUse()
{
int i = 123;
Console.WriteLine("{0}, sizeof(short));
Console.WriteLine("{0}, sizeof(i));
}
- General
- Indexer methods
- LINQ to Objects
- General
- "The term 'LINQ to Objects' refers to the use of LINQ queries with any IEnumerable or IEnumerable
collection directly, without the use of an intermediate LINQ provider or API such as LINQ to SQL or LINQ to XML." MSDN > Library > Development Tools and Languages > Visual Studio 2015 > C# And Visual Basic Shared Programming Concepts > LINQ (Language-Integrated Query) > LINQ to Objects - Typical enumerable collections on which you will use LINQ to Objects
- A simple
Array
List<T>
Dictionary<TKey, TValue>
- A simple
- Prior to LINQ query logic had to be embedded in
foreach
loops - Especially as the complexity of your query logic grows LINQ to Objects is a big advantage over working with
foreach
loops
- "The term 'LINQ to Objects' refers to the use of LINQ queries with any IEnumerable or IEnumerable
- LINQ overview
- General
- LINQ introduced with .NET 3.5
- Prior to LINQ .NET programmers had to use a variety of APIs, based on type of data they wanted to access
- Relational data:
System.Data.dll
System.Data.SqlClient.dll
- Others
- XML document data:
System.Xml.dll
- Assembly metadata:
System.Reflection
namespace - Collections:
System.Array
System.Collections
namespaceSystem.Collections.Generic
namespace
- Relational data:
- Will still make use of these assemblies, types, & namespaces
- LINQ goes a long way, however, to standardize data retrieval & manipulation code
- "Types" of LINQ:
- LINQ to Objects
- LINQ to XML
- LINQ to DataSet
- LINQ to Entities
- Parallel LINQ
- A huge benefit of LINQ expressions is that they are strongly typed
- LINQ-specific programming constructs
- Implicitly typed local variables
- Initializer syntaxes
- Lambda expressions
- Extension methods
- You rarely write extension methods to work with LINQ
- However, LINQ expressions are, in fact, shorthand calls on underlying extension methods
- Most of these methods are in
System.Linq.Enumerable
utility class
- Anonymous types
- The core LINQ assemblies
System.Core.dll
System.Data.DataSetExtensions.dll
System.Xml.Linq.dll
- Note: When using LINQ make sure your code has imports the
System.Linq
namespace
- General
- Applying LINQ parameters to primitive arrays
- General
- Assume you are working with a string array
- Syntax/example:
string[] someBluths =
{"Lindsey", "Tobias", "Michael", "George Michael"}; - What's interesting about getting
string
results from a LINQ expression is theIEnumerable<string>
expression - Syntax/example:
IEnumerable<string> mBluths =
from b in someBluths
where b.Contains("Michael")
orderby b select b;
- Querying without LINQ
- Certainly doable, though more verbose
- You are forced to use loops &
if
statements - See Troelsen: 5th Ed., pp. 497 - 8, for a non-LINQ, "longhand" version of his LINQ example
- Reflecting over a LINQ result set
- In above example
mBluths
is of typeOrderedEnumerable<TElement, TKey>
- In CIL
OrderedEnumerable<TElement, TKey>
will show asOrderedEnumerable`2
- "Many of the types that represent a LINQ result are hidden by the Visual Studio 2010 object browser." (Troelsen: 5th Ed., p. 498)
- Can, however, use
ildasm.exe
orreflector.exe
to see these hidden types
- In above example
- LINQ & implicitly-typed local variables
- Unlike the above example, you often want to use LINQ on datasets where you do not know at design time what the underlying type will be
- Even if you do know the underlying type of the LINQ-populated subset the resulting type can be maddening to anticipate
- For instance…
- Assume you run a LINQ query on an
int
array - You will define the subset as
IEnumerable<int>
- The type which implements the
IEnumerable<int>
interface is a low-level class calledWhereArrayIterator<T>
!!
- Assume you run a LINQ query on an
- Rather than capture your LINQ results as an
IEnumerable<T>
generic it is much easier to catch the results as an implicitly-typed local variable - Syntax/example:
// Preliminary code…
var mBluths = from b in someBluths
where b.Contains("Michael") orderby b select b; - Syntax/example (to iterate over LINQ results captured as implicitly-typed local variable):
// Preliminary code…
for (var b in mBluths)
{ // Iteration code… } - Notes:
- Even when you use an implicitly-typed local variable the real return type is, in fact, an
IEnumerable<T>
generic - Troelsen claims (5th Ed., p. 500) that there is no need to worry about what the true underlying type of your
var
is
- Even when you use an implicitly-typed local variable the real return type is, in fact, an
- LINQ & strongly-typed implicitly-typed local variables
- This can be done, simply by specifying the type in your LINQ query
- Syntax/example:
class Program
{
static void Main(string[] args)
{
// Populate array of Bluths…
Bluth[] theBluths = GetBluths();
// Strongly-type the LINQ query…
var query = from Bluth b in theBluths
where b.Age > 18
select b;
foreach (Bluth b in query)
Console.WriteLine(b.FirstName + ": " + b.Age);
Console.ReadLine();
}
static Bluth[] GetBluths()
{
Bluths[] arrBluths = new Bluths[]
{
new Bluth
{
FirstName = "George",
LastName = "Bluth",
Age = 65
},
// Add more Bluths…
};
return arrBluths;
}
}
class Bluth
{
internal string FirstName { get; set; }
internal string LastName { get; set; }
internal int Age { get; set; }
}
- LINQ & extension methods
- Note that the
Array
type does not implement theIEnumerable
interface - Instead, the
System.Linq.Enumerable
static class adds this interface (as well as others) as extension methods - Use IntelliSense on your
var
subset variable to identify the various interfaces & methods added as extensions
- Note that the
- The role of deferred execution
- LINQ results are not actually run/evaluated until you iterate over the results!
- Therefore, if there is some change to the data upon which you are running your LINQ query…
- You do not need to re-execute the LINQ statement
- Simply re-running your
foreach
(or appropriate iterator) statement will give you the accurate results
- Note: setting a breakpoint at a LINQ query line whilst debugging allows you to step through the query results
- The role of immediate execution
- You may not always want to use a
foreach
statement to evaluate the results of a LINQ query - There are a number of extension methods available here:
ToArray<T>()
ToDictionary<TSource, TKey>()
ToList<T>()
- Others
- To invoke these methods you typically wrap the entire LINQ statement within parenthesis, & then simply invoke the dot operator
- Syntax/example:
static void ImmediateExecution()
{
int[] lostNumbers = { 4, 8, 15, 16, 23, 42 };
// Populate 2 sets of data NOW…
int[] intArrayLowNos =
(from i in lostNumbers where i < 10 select i).ToArray<int>();
List<int> intListLowNos =
(from i in lostNumbers where i < 10 select i).ToList<int>();
} - Notes:
- Wrapping the LINQ statement in parenthesis serves to cast the results of the statement in 'the' appropriate type (though you really don't care what that type is!)
- As discussed in Generics, if the compiler 'knows' that, say, the underlying type is an
int
- You do not even need to specify type in your code
- Specifically, you could write
( … ).ToArray();
instead of( … ).ToArray<int>();
- Now your result set can be independently manipulated
- Immediate execution is required when you need to pass the results of your LINQ statement to an external caller
- You may not always want to use a
- General
- Returning the result of a LINQ query
- General
- Results of LINQ queries rarely used to instantiate a field
- You cannot define a field as being of type
var
- The target of a LINQ query cannot be instance-level data (i.e., it must be static data)
- You cannot define a field as being of type
- Typically LINQ queries are scoped within a method or property
- The
var
keyword creates some issues, however, because an implicitly-typed local variable cannot be used to define- Parameters
- Return values
- Fields (as just mentioned)
- One way to return the result of a LINQ query to an external caller is to avoid using the
var
keyword & give your method a return type of, say,IEnumerable<string>
- Results of LINQ queries rarely used to instantiate a field
- Returning LINQ results via immediate execution
- The other approach is to use immediate execution
- Give your method a return type of, say,
string[]
- Inside your method capture the results of your LINQ query in a
var
type - Then invoke one of the extension methods on the
var
type to return, say, an array - Syntax/example:
static string[] GetMichaelBluthsAsArray()
{
string[] someBluths = {"Lindsey", "Tobias",
"Michael", "George Michael"};
// Capture results of LINQ query in a var…
var mBluths = from b in someBluths
where b.Contains("Michael") select b;
// Invoke immediate execution, cast as string array…
return mBluths.ToArray();
}
- General
- Applying LINQ queries to collection objects
- Accessing contained sub-objects
- Recall that LINQ to Objects can be used on any type implementing
IEnumerable<T>
- Therefore, applying LINQ to collections is no different from applying to arrays (done above)
- Recall that LINQ to Objects can be used on any type implementing
- Applying LINQ queries to nongeneric collections
- Although the LINQ extension methods have applied
System.Array
with anIEnumerable<T>
implementation, the other members ofSystem.Collections
have not been similarly outfitted - You can still apply LINQ statements to these other non-generic collections
- You apply the
Enumerable.OfType<T>()
extension method Enumerable.OfType<T>()
is one of the few extension methods that does not extend generic types- So long as you are working with a collection object which implements
IEnumerable
" … simply specify the type of item within the container to extract a compatibleIEnumerable<T>
object." (Troelsen: 5th Ed., p. 507) - Even better, you use an implicitly-typed local variable to capture that casting
- You apply the
- Syntax/example:
static void UsingLINQOnArrayList()
{
ArrayList someBluths = new ArrayList() { // populate w\ Bluths… }
// Transform ArrayList into an IEnumerable<T>-compatible type…
var enumBluths = someBluths.OfType<Bluth>();
// Now run LINQ…
var mBluths = from Bluth in enumBluths…
}
- Although the LINQ extension methods have applied
- Filtering data using
OfType<T>()
- Remember that non-generics receive
System.Object
types - i.e., anything - A big advantage of using
OfType<T>()
is that when moving data from your non-generic to your generic the method automatically filters your data for you
- Remember that non-generics receive
- Accessing contained sub-objects
- The C# LINQ query operators
- General
- Various LINQ operators
from, in
where
select
join, on, equals, into
orderby, ascending, descending
group, by
- There are also any number of LINQ extension methods, such as
- To transform result sets
Reverse<>()
ToArray<>()
ToList<>()
- To extract singletons or to perform other set calculations
Distinct<>()
Union<>()
Intersect<>()
- To aggregate results
Count<>()
Sum<>()
Min<>()
Max<>()
- To transform result sets
- Various LINQ operators
- Basic selection syntax
- Very similar to SQL syntax
- Difference: In LINQ the
select
statement comes at the end of the entire query statement
- Obtaining subsets of data
- As with SQL you use a
where
clause to filter your query - For compound
where
clauses use any valid C# operator (e.g.,&&, ¦¦, <
, etc.) - Syntax/example:
// Preliminary code…
var adultMichaelBluths = from b in Bluths
where b.name.Contains("Michael") && b.age > 18 select b;
- As with SQL you use a
- Projecting new data types
- Create a
select
statement that creates a new, anonymous type - Suppose you have an array of
Employee
objects, whereEmployee
contains not just name but birthdate, id number, start date, & more - From that you simply want a list of employees
- Syntax/example:
static void GetAlphaListOfEmployees(Employee[] arrEmpls)
{
var alphEmpls = from e in arrEmpls
select new { e.LName, e.FName };
} - Limitations
- The type you will be creating will not exist until compile time, so you must 'capture' that type as a
var
- Again, you cannot create a method which returns a
var
- The type you will be creating will not exist until compile time, so you must 'capture' that type as a
- Obtaining results from a method which projects data types
- Once you have created & populated your
var
variable you can invoke theToArray()
extension method on that variable - The resulting array is, of course, something you can return from a method
- Because you again do not know the underlying type being created until compile time you cannot invoke
ToArray<T>()
(or create other generic containers)
- Once you have created & populated your
- Final word on projecting new data types
- In returning an
array
(or any other non-generic container type which implementsIEnumerable
) you are left with a somewhat unsatisfying collection ofobject
types - An
object
, of course, can be anything, so you lose strongly-typed data - There simply is no other option
- In returning an
- Create a
- Obtaining counts using
Enumerable
- Use the
Count()
method of theEnumerable
class - Syntax/example:
static int NumberMichaelBluths(string[] someBluths)
{
return (from b in someBluths
where b.Contains("Michael") select b).Count<string>();
}
- Use the
- Reversing result sets
- Use the
Reverse<T>()
extension method of theEnumerable
class - Syntax/example:
static void DescendingListEmployees(Employee[] ourEmpls)
{
var allEmpls = from e in ourEmpls select e;
foreach (var empl in allEmpls.Reverse())
{ // Do something… }
}
- Use the
- Sorting expressions
- Use the
orderby
keyword - You typically use it on a property - e.g.,
from e in ourEmpls orderby e.LName select e;
- Can specify
ascending
(default) ordescending
- Use the
- LINQ as a better Venn diagramming tool
Enumerable
class has a number of set-based methods:Except()
Intersect()
Union()
- follows set theory - i.e., no duplicate membersConcat()
- this will not eliminate duplicates
- Syntax/example:
var diff = (some LINQ query).Except(second LINQ query);
- Removing duplicates (e.g., from a
Concat()
expression)- Use
Distinct()
method - Syntax/example:
static void RemoveDupesFromConcat()
{
// Assume two overlapping List<string> of Bluths has been created…
var concatBluths = (from b in firstBluths select b)
.Concat(from b2 in secondBluths select b2);
foreach (string s in concatBluths.Distinct())
{ // Some code… }
}
- Use
- LINQ aggregation operations
- In addition to
Count()
method you also have available:Max()
Min()
Average()
Sum()
- As with Count(), invoke these on parenthetically-wrapped LINQ queries
- In addition to
- General
- Internal representation of LINQ query statements
- General
- Behind the scenes, "the C# compiler actually translates all C# LINQ operators into calls on methods of the
Enumerable
class." (Troelsen: 5th Ed., p. 517) Func<>
delegate- Defined within
System.Core.dll
assembly - This delegate "represents a pattern for a given function with a set of arguments and a return value." (Troelsen: 5th Ed., p. 518)
- Takes between zero & four arguments and then a return type
- Format for 2 arguments:
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)
- Format for no arguments:
public delegate TResult Func<TResult>()
- This delegate is important because many members of
System.Linq.Enumerable
require a delegate as input - Options when invoking these members
- Create a new delegate type, along with target method(s)
- Use an anonymous method
- Use/define a lambda expression
- Note: options all lead to identical results
- Defined within
Func<>
delegate is the delegate most frequently required byEnumerable
methods- Example:
Where()
method ofEnumerable
- This is the method called whenever you use the LINQ
where
query operator Where()
method is overloaded, & second parameter of each method signature is of typeSystem.Func<>
- Definition 1:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
System.Func<TSource, int, bool> predicate) - Definition 2:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
System.Func<TSource, bool> predicate)
- This is the method called whenever you use the LINQ
- While LINQ query operators are by far the simplest way to author LINQ, it is possible to use the approaches delineated below
- Behind the scenes, "the C# compiler actually translates all C# LINQ operators into calls on methods of the
- Building query expressions using the
Enumerable
type & lambda expressions- Syntax/example:
class Program
{
static void Main(string[] args)
{
string[] favBands = { "Beatles", "The Who",
"Foo Fighters", "Weazer", "Pearl Jam" };
QueryBands(favBands);
}
static void QueryBands(string[] rockGroups)
{
// Build a query expressing using extension methods
// granted to the Array via the Enumerable type…
var subset = rockGroups.Where(band => band.Contains(" "))
.OrderBy(band => band).Select(band => band);
// Print out results…
foreach (var band in subset)
Console.WriteLine("Band I like: {0}", band);
Console.ReadLine();
}
} - Notes:
- 'Starting point' is invocation of
Where()
extension method - In turn,
Enumerable.Where()
method requires aSystem.Func<T1, TResult>
delegate parameter - Delegate parameters:
- First:
IEnumerable<T>
compatible data - i.e., the array of strings, which are the data to be processed - Second: the results from the method, "which is obtained from a single statement fed into the lambda expression" (Troelsen: 5th Ed., p. 519)
- First:
Where()
method's return value- Not apparent from the code
- In fact, though, it is an
OrderedEnumerable
type
OrderedEnumerable
type in turns calls genericOrderBy()
methodOrderBy()
, not surprisingly, takes aFunc<>
delegate parameter- You fill out that delegate requirement with a lambda expression
- Last portion is the call to the
Select()
method - Above example - rewritten to show all steps:
var bandsWithSpaces = rockGroups.Where(band => band.Contains(" "));
var bandsInOrder = bandsWithSpaces.OrderBy(band => band);
var subset = bandsInOrder.Select(band => band);
foreach (var band in subset)
Console.WriteLine("Band I like: {0}", band);
- 'Starting point' is invocation of
- Syntax/example:
- Building query expressions using the
Enumerable
type & anonymous methods- C# lambda expressions are shorthand notations for anonymous methods
- One can then reconstruct LINQ queries using anonymous methods
- Syntax/example:
static void QueryBands(string[] rockGroups)
{
// Build the necessary Func<> delegates using anon methods…
Func<string, bool> searchFilter =
delegate(string band) { return band.Contains(" "); };
Func<string, string> itemsToProcess =
delegate(string s) { return s; };
// Pass the delegates into the methods of Enumerable…
var subset = rockGroups.Where(searchFilter)
.OrderBy(itemsToProcess).Select(itemsToProcess);
// Print out results…
foreach (var band in subset)
Console.WriteLine("Item: {0}", band);
Console.ReadLine();
}
- Building query expressions using the
Enumerable
type & raw delegates- Can always 'go the long way round the barn'
- Syntax/example:
static void QueryBands(string[] rockGroups)
{
// Build the necessary Func<> delegates…
Func<string, bool> searchFilter =
new Func<string, bool>(Filter);
Func<string, string> itemToProcess =
new Func<string, string>(ProcessItem);
// Pass the delegates into the methods of Eunumerable…
var subset = rockGroups.Where(searchFilter)
.OrderBy(itemToProcess).Select(itemToProcess);
// Print out results (as above)…
}
// Delegate targets…
static bool Filter(string band
{ return band.Contains(" "); }
public static string ProcessItem(string band)
{ return band; }
- Final notes/summary
- Under the hood LINQ query operators do nothing more than invoke extension methods defined by
System.Linq.Enumerable
type - Many of these methods take delegates - typically
Func<>
- as parameters - Can always pass in a lambda expression in lieu of a delegate parameter
- Under the hood LINQ query operators do nothing more than invoke extension methods defined by
- General
- General
- Covariance & contravariance (aka, variance)
- Programming with .NET Assemblies
- Configuring .NET Assemblies
- Defining custom namespaces
- General
- Creating namespaces
- Can define a single namespace across multiple files/classes within a single assembly
- Can define a single namespace across multiple assemblies (this is done with certain .NET namespaces)
- My namespace
- Auto-generated by VS (at least in VB)
- Provides access to machine and project resources
- VS also seems to create a child namespace,
My.Resources
- References, & importing custom namespaces
*.C#
files within project tab of entire project determines which resources are available for all- When accessing a type in another namespace within the same project must still either
- Fully qualify type reference (i.e.,
rootnamespace.childnamespace1…
) - Include
using
statement(s) at top of file
- Fully qualify type reference (i.e.,
- Creating namespaces
- Name clashes
- In C# will get a compiler error if you refer to "a" type that exists in more than 1 namespace
- Resolving name clashes using fully-qualified names
- You are actually not required to employ the
using
keyword - Can fully qualify type reference:
rootnamespace.childnamespace1… &.typename
- However, generally little sense in eschewing
using
keywordusing
keyword saves keystrokes- In CIL code types are always defined with fully-qualified names
- Only time where it makes sense to use fully-qualified names is when you need to resolve name clashes (i.e., you have identically-named types in more than one namespace)
- You are actually not required to employ the
- Resolving name clashes using aliases
- Syntax:
using someAliasName = Rootnamespace.ChildNamespace.TypeYouWant
- An alias name is simply a token, or placeholder, that will be replaced by fully-qualified namespace at runtime
- May also want to use aliases in lieu of lengthy/deep namespace declarations
- Syntax:
- Creating nested namespaces
- Can create additional namespaces via the
namespace
keyword - These namespaces are children of the root namespace
- Can nest namespace declarations in code
- Sometimes the role of a root namespace is simply to provide scope
- i.e., there are no types defined within the root namespace
- In this case can omit
namespace
declaration at root level - Shortcut syntax:
namespace MyRootNamespace.SomeChildNamespace
- Can create additional namespaces via the
- VS & default namespaces
- VS creates a default namespace (also called the root namespace) for each assembly
- Root namespace set in project window in VS
- Default name for root namespace is the name of project itself
- Name of root namespace can be changed
- General
- .NET assemblies
- Role
- General
- .NET assembly: "a versioned, self-describing binary file hosted by the CLR" (Troelsen: 5th Ed., p. 532)
*.exe
or*.dll
file extensions
- Code reuse
- Executable assemblies can certainly make use of defined types in other executable assemblies
- In other words, an executable can be considered/set up as a code library
- Generally, though, one creates
.dll(s)
for this - Language neutrality
- Type boundaries
- Another layer of identification above namespaces
- Full qualification of a class =
assembly.parentnamespace.childnamespace.granchildnamespace…class
- Versionable units
- Version number format: major.minor.build.revision
- When used in conjunction with (optional) public key value allows multiple versions to exist on same machine
- Note: When a public key value is used the assembly is termed strongly named
- Self-describing (via manifest)
- So-called because .NET assemblies record every external assembly they require
- Manifest also describes composition of every contained type
- Because of manifest .NET assemblies, unlike COM assemblies, do not need to consult Windows system registry
- Configurable
- Private vs. shared assemblies
- Private assemblies exist in directory or sub-directory of the consuming application
- Shared assemblies reside in global assembly cache, aka GAC
*.config
file (XML)
- Private vs. shared assemblies
- General
- Format (i.e., .NET assembly file components)
- Win32 file header
- Establishes that an assembly can be run under Windows OS
- Defines kind of app (console vs. GUI vs.
*.dll
code library) - To view: run
dumpbin.exe
at VS command prompt and include/headers
flag
- CLR file header
- Block of data all .NET files must support
- Automatically generated by VS compiler
- Defines flags that enable the runtime to understand the layout of the assembly
- Sample flags: location
- Location of the metadata
- Location of resources within the file
- Version of the runtime the assembly was built against
- To view: run
dumpbin.exe
at VS command prompt and include/clrheader
flag - Note: somewhat ironically, data in CLR file header are represented by an unmanaged C-style structure
- CIL code, type metadata, & the assembly manifest
- CIL code is, of course, the core of an assembly
- Again, CIL is platform- & CPU-agnostic
- CIL compiled at runtime (by JIT-er) according to CPU & platform instructions
- Metadata - completely describes format of:
- Internal types
- External types referenced by assembly
- Metadata used to:
- Resolve location of types (& of types' members) within the binary
- Place types into memory
- Facilitate remote method invocations
- Assembly manifest documents/specifies:
- Each module within an assembly
- Assembly version
- References to external types
- CIL code is, of course, the core of an assembly
- Optional assembly resources (embedded)
- These can be images, icons, etc.
- If you have a lot of these kinds of items you can place them in satellite assemblies
- Multi-file assemblies
- Note: most .NET assemblies are single-file
- Module: generic term for a valid .NET binary file
- Multi-file assemblies: multiple .NET
*.dlls
deployed & versioned as a single logical unit - One module will be the primary module
- Primary module contains the assembly-level manifest (plus CIL code, metadata, etc.)
- Secondary modules
- Naming convention (not a requirement):
*.netmodule
file extension - Contain CIL code, type metadata, and module-level manifest
- Naming convention (not a requirement):
- Advantages of multi-file assemblies
- Fast download times
- Development flexibility
- Win32 file header
- Role
- Building & consuming assemblies
- Single-file
- The manifest in
ildasm.exe
- First code block specifies all external assemblies referenced
.publickeytoken
will be present only for those external assemblies with a strong name.custom
tokens then follow- These identify assembly-level attributes
- These tokens come from (hidden)
AssemblyInfo.cs
file created by VS - Identify items such as company name, trademark, etc.
- Set assembly-level attributes through button on tab of Solution screen
- Manifest also includes
.assembly
&.module
tokens, which include names of those 2 items
- Exploring the CIL
- Continue using
ildasm.exe
- Can get appropriate details under following tags:
.method
tags.field
tags.property
tags
- Continue using
- Exploring the type metadata (
ildasm.exe
to view) within - Note: language-independence of .NET platform allows for cross-language inheritance
- The manifest in
- Multi-file
- General
- VS does not support a multi-file assembly template
- Ergo, must use command-line compiler (
csc.exe
) to create a multi-file assembly- Use the
/t
flag to create a*.netmodule
file - Note: you want to compile any/all secondary modules first, as you will need a reference to them when compiling the primary module
- Will need to use the following flags when compiling primary module from
csc.exe
/t:library
/addmodule:
- then name of secondary module (e.g.,/addmodule:file2.netmodule
)/out:
- then name of primary module (e.g.,/out:mymainassembly.dll
)- Last argument on command line is name of main
*.cs
file
- Use the
- Can use
ildasm.exe
to explore a*.netmodule
just as you would do with any other .NET assembly - Files are not merged into a single
*.dll
- Main
*.dll
&*.netmodule
s are 'stitched together' via the assembly maifest
- Consuming a multi-file assembly
- When referencing you only need to supply the name of the primary module
- Notes re:
*.netmodule
files…- Do not have individual version numbers
- Cannot be loaded directly by CLR - can only be loaded via primary module
- General
- Single-file
- Understanding assemblies
- Private
- General
- Private assemblies must be located within the directory (called the application directory) or in a sub-directory of the client app
- Note: When you browse to a non-public (i.e., private) assembly when setting project references VS automatically copies that assembly into the client application's
\bin\Debug
folder - Client app does not have to consult Windows registry for location of private assemblies
- This all means that .NET apps and their private assemblies can be relocated on a machine
- Without re-installing
- Without affecting any other application
- Without creating orphaned registry entries
- Moving and/or copying an application and its private assemblies like this is called Xcopy deployment
- Lastly, to uninstall a .NET assembly:
- Can simply delete application directory (& all sub-directories)
- You will not be left with orphaned registry settings!
- Identity of a private assembly
- Friendly name: (primary) module name minus the file extension
- Full identity = friendly name plus numerical version (both are in assembly manifest)
- Because private assemblies are isolated CLR does not use version number when checking for location
- Probing process
- Probing = mapping external assembly request to location of requested binary file
- Implicit request
- CLR consults manifest to find assembly
- CLR refers to
.assembly extern
token to do this
- Explicit request
- Programmatic request, via
Load()
orLoadFrom()
method - These methods are in the
System.Reflection.Assembly
.NET class - Usually done for late binding & dynamic loading of type members
- Programmatic request, via
- How probing works
- CLR begins with friendly name, then appends
.dll
- CLR looks for that file in application directory
- If unsuccessful CLR looks for a subdirectory with the exact name as the assembly's friendly name… and looks there for the file
- If still unsuccessful CLR repeats process but for a file = friendly name plus
.exe
extension - If then unsuccessful CLR throws a
FileNotFoundException
object at runtime
- CLR begins with friendly name, then appends
- Configuring private assemblies
- Can control where CLR looks for private assemblies by creating a configuration file
- Configuration files are XML files
- Configuration file must take the same name as the launching application, & then take
*.config
file extension - Example:
MyApplication.exe.config
- Configuration files must be deployed in client's application directory
- Configuration files allow assemblies to be deployed into sub-directories
- The
App.config
file- Root node:
<configuration>
- Next 2 nested elements:
<runtime>
&<assemblyBinding>
<probing>
element- Use (embedded)
privatePath
attribute - Note:
privatePath
attribute does not specify which assemblies are located where privatePath
attribute only specifies where private assemblies may be found- Can specify multiple paths with
privatePath
attribute via semi-colon-delimited values - Can only specify sub-directories with
privatePath
attribute (i.e., can specify neither relative nor absolute directories) - Notes:
- If multiple copies/versions of an assembly exist in app directory & subdirectories CLR will load the 1st one it finds
- To specify external directories must use
<codeBase>
element
- Use (embedded)
- Example:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyLib;MyLib\OtherLibs" />
closing tags…
- Root node:
- Can control where CLR looks for private assemblies by creating a configuration file
- Configuration files & Visual Studio
- To add:
- Default name =
app.config
- do not change! - When the app is compiled VS will copy the data in
app.config
into the\bin\Debug
directory and will properly name/rename that*.config
file - Ergo, this process will work even if project is renamed
- General
- Shared
- General
- Single copy of a shared assembly can be used by several applications on a single machine
- Shared assemblies are deployed to the GAC
- Can only install
*.dll
assemblies in the GAC (i.e., cannot install*.exe
's)
- Strong names
- An assembly must be assigned a strong name before it can be deployed to the GAC
- Uniquely identifies publisher of a .NET assembly
- Roughly akin to globally unique identifier (GUID) scheme used by COM
- Formal definition/requirements of a strong name
- Friendly name of the assembly
- Version number (assigned using the
[AssemblyVersion]
attribute insideAssemblyInfo.cs
file) - Public key value (assigned using the
[AssemblyKeyFile]
attribute insideAssemblyInfo.cs
file) - digital signature: created using a hash of the (entire) assembly's contents & its private key value
- Optional: cultural identity value (assigned using the
AssemblyCulture
attribute)
- Strong names are based (in part) on 2 cryptographically related keys:
- public key
- private key
*.snk
file contains the data for the public & private keys*.snk
file is not the strong name itself*.snk
simply pairs the public and private key data
- Once you let compiler know location of
*.snk
file- At compile time records full public key value in assembly manifest
- Identifies in manifest via
.publickey
token
- Digital signature
- Equals combination of hash code & private key
- Note: a hash code is a numerical value that's unique for a given input
- If you change so much as a comma in your code the compiler will generate a new, unique hash code
- Gets embedded in assembly's CLR header data
- Private key
- Not found anywhere inside assembly manifest
- Used to create digital signature
- Equals combination of hash code & private key
- .NET Best Practice: strongly name all assemblies, even private ones
- An assembly must be assigned a strong name before it can be deployed to the GAC
- Creating
*.snk
(Strong Name Key) file- Note: Initially-created
*.snk
files are empty until assembly is compiled - Method 1:
sn.exe
command line utility- Not used much any more
-k
flag instructs tool to generate a new file using public/private key info- Syntax:
sn -k MyKeyPair.snk
- Using the
*.snk
file- Under
AssemblyInfo.cs
file open the - Add the appropriate
[AssemblyKeyFile]
assembly-level attribute - Syntax/example:
[assembly: AssemblyKeyFile(@"C:\Users\Bill\Documents\MyTestKeyPair.snk")]
- Note: Ignore the warnings you might receive
- Under
- Version numbers
- Format:
<major>.<minor>.<build>.<revision>
- Each part can take a value between 0 and 65535
- By default
AssemblyInfo.cs
will contain the following attribute:
[assembly: AssemblyVersion("1.0.0.0")]
- However, you can use the wildcard token to instruct VS to increment build & revision numbers automatically:
[assembly: AssemblyVersion("1.0.*")]
- Format:
- Method 2: VS 2010
- Navigate to tab of project's page
- Check box
- Under select or
- If creating a new file
- Remember to append
.snk
file extension - Can password-protect the
*.snk
file *.snk
file will now appear in VS
- Remember to append
- As you generate new versions re-use the same
*.snk
file - Reminder…
- Can navigate to button via
- Here you can set assembly-level attibutes, such as copyright information, etc.
- Note: Initially-created
- Installing strongly-named assemblies to the GAC
- Preferred methods in production - use either
- Windows MSI installer package
- A 3rd-party installer package, such as InstallShield
- For testing use
gacutil.exe
command-line utility- Note: requires admin rights
- Can use the
/?
flag to see all options/flags - Most frequently-used flags
/i
- for installing a strongly-named assembly to the GAC/u
- for uninstalling an assembly/l
- for listing assemblies in the GAC
- Navigate to directory containing the
*.dll
you want to install - Syntax/example - installing a library:
gacutil -i MyLibrary.dll
- Syntax/example - to verify installation:
gacutil -l MyLibrary
- Preferred methods in production - use either
- Viewing the .NET 4.0 GAC using Windows Explorer
- Prior to .NET 4.0 the GAC was
C:\Windows\assembly
- Now, however, the GAC is bifurcated
- All pre-.NET 4.0 base class library continue to reside in
C:\Windows\assembly
C:\Windows\Microsoft.NET\assembly\GAC_MSIL
- This is where all .NET 4.0+ shared assemblies are located
- Within this there are numerous sub-directories
- One for each code library - including BCLs
- Sub-directory name = friendly name of assembly
- Under these sub-directories are more sub-directories
- Naming convention:
v4.0_major.minor.build.revision_publicKeyTokenValue
- Note: the
v4.0
signifies that the library was compiled against .NET 4.0
- Naming convention:
- All pre-.NET 4.0 base class library continue to reside in
- Prior to .NET 4.0 the GAC was
- General
- Private
- Shared assemblies
- Consuming
- Browsing to assembly location from within VS
- After adding reference to a shared assembly you can see that
False
is set to - (Ergo, shared libraries are not copied to consuming app's
/bin/debug
folder)
- After adding reference to a shared assembly you can see that
- The manifest of a shared assembly
- Public key token recorded in manifest will exactly match public key token in assembly's parent folder in the GAC
- If the assembly is not found in the GAC VS will look for a local (i.e., private) copy of the assembly
- A
FileNotFoundException
is thrown if that fails
- Browsing to assembly location from within VS
- Configuring
- General
- Shared assemblies can be configured via a client
*.config
file- Same as with private assemblies
<privatePath>
element- Not used for shared assemblies because, of course, public assemblies by definition reside in the GAC
- If there is even one private assembly, however, the element might still exist in the
*.config
file
- Working with multiple versions of a shared assembly
- Scenario 1: You discover a bug in version 1.0.0.0 of a code library you have in your GAC
- Assume you distribute corrected code library as version 1.1.0.0
- Distribute a new
*.config
file pointing clients to version 1.1.0.0 of code library
- Scenario 2: You add new functionality & release version 2.0.0.0 of your code library
- Existing client apps will have no knowledge of version 2.0.0.0
- Both versions can reside next to each other in GAC
- New versions of client app can complile against code library 2.0.0.0 & older versions can continue to comple against 1.0.0.0
- Scenario 1: You discover a bug in version 1.0.0.0 of a code library you have in your GAC
- Shared assemblies can be configured via a client
- New versions of a shared assembly
- Building
- After making code modifcations update via
- Reuse existing
*.snk
file (again, visible in ) - Install new version of assembly in GAC
- Inside GAC
- Will now see a new version-specific subdirectory in GAC for updated assembly
- If you used same
*.snk
file the new version of assembly will have same public key (which is what you want!)
- Client apps
- If you recompile client apps VS will automatically reset references to the new version of the shared assembly
- However, if you do not change client app(s) they will continue to point to original shared assembly
- Building
- Dynamically redirecting to specific versions of a shared assembly
- If, as is often the case, you do not want to recomple a client app to use a specific (generally new) version of a shared assembly you modify the client app's
*.config
file - Note: what you are doing with this approach is overriding the client assembly's manifest
- Main steps
- Crack open the client app's
*.config
file (e.g.,MyClientApp.exe.config
) - As with addition of
<probing>
element, within root<configuration>
element nest a<runtime>
element - Within
<runtime>
element:- Nest a
<assemblyBinding>
element - Within that (opening) element include a
xmlns="urn:schemas-microsoft-com:asm.v1"
namespace declaration
- Nest a
- Within
<assemblyBinding>
scope include<dependentAssembly>
(pairs of) elements for each shared assembly you need to control version-use for
- Crack open the client app's
- Inside each
<dependentAssembly>
scope include 2 empty elements:<assemblyIdentity>
- & include following attributes & values:name="SharedAssemblyFriendlyName"
publicKeyToken="token…"
culture="neutral"
(can pick values other than"neutral"
)
<bindingRedirect>
- & include following attributes:oldVersion
(set value equal to old version number, e.g., 1.0.0.0)newVersion
(set value equal to new version number, e.g., 2.0.0.0)- Note: can specify a range for
oldVersion
values - e.g.,"1.0.0.0-1.5.0.0"
- Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="CarLibrary"
publicKeyToken="8fc57dba00ce65d2"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-1.2.0.0"
newVersion="2.0.0.0" />
closing tags…
- Note: I could not get this whole approach to work! (following example in Troelsen: 5th Ed., pp. 569-74)
- If, as is often the case, you do not want to recomple a client app to use a specific (generally new) version of a shared assembly you modify the client app's
- Publisher policy assemblies
- General
- The just-described dynamic redirection approach only controls a single installation of a client app
- You obviously will often run into situations where you want all instances of client apps to bind to a new version of a shared library
- Publisher policy enables you to install a binary version of a
*.config
file in the GAC- Much easier than configuring & deploying
*.config
files to each client machine - Note: over time you can easily lose track of which machines and/or apps even use a shared library
- Removes the need even to have those
<assemblyBinding>
elements in a client*.config
file
- Much easier than configuring & deploying
- Features of a publisher policy assembly
- This file is a binary/assembly unto itself
- The binary is deployed in the GAC, 'alongside' the shared assembly
- Note: because the assembly is deployed in the GAC the assembly must itself be strongly named!
- The CLR & publisher policy assemblies
- When working with the client app CLR will 1st begin looking in GAC, following the app's manifest
- If, however, the CLR 'sees' a relevant publisher policy binary the CLR will follow the redirection specified in that binary
- Creating a publisher policy assembly
- One still employs XML for the raw content
- Create the binary via the
al.exe
("al" stands for "assembly linker") command-line tool - Must supply the following input parameters:
- Location of the
*.config
or*.xml
file which stores the redirection info - Name you want to give the resulting binary
- Location of the
*.snk
file used to sign the publisher policy assembly - Version number(s) for the publisher policy assembly
- Location of the
- Syntax/example (in command window must be all on one line):
al /link: MySharedLibPolicy.xml
/out:policy.1.0.MySharedLib.dll
/keyf:C:\MyKeys\MyPubPolKey.snk /v:1.0.0.0 - Note: After creating a publisher policy assembly via the
al.exe
tool it appears that you must still deploy that assembly to the GAC (if so, you of course use thegacutil.exe
tool)
- Disabling publisher policy
- Scenario
- You have 10 client apps relying on version 1.0 of a shared library
- You upgrade shared library to version 2.0
- You deploy a publisher policy assembly to direct all client apps to use version 2.0 of the shared assembly
- However, after doing so 1 of your 10 client apps breaks
- Solution
- You want to 'roll back' that one troublesome client installation to use version 1.0 of the shared library
- Approach
- Add an empty
<publisherPolicy>
element to the client's*.config
file - Inside the element set the
apply
attribute to"no"
- Syntax/example:
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="no" />
closing tags…
- Add an empty
- Scenario
- General
- General
- Consuming
- Miscellaneous
app.config
file topics<codeBase>
element- This element allows you to direct your client app to search for dependent assemblies in arbitrary locations
- If the assembly is located on a remote machine
- The dependent assembly will be copied to the download cache
- This is a specific directory in the GAC
- To view the directory execute this in the command window:
gacutil /ldl
- Strong names
- Not surprisingly, this process will only work if the remote assembly is strongly named
- Note: that said, in certain circumstances you can work with assemblies that lack a strong name
- The assembly must be in a sub-directory of client's app directory
- In effect, though, this is simply an alternative to using the
<privatePath>
element
- Syntax/example:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyLib" publicKeyToken="xyz…" />
<codeBase version="2.0.0.0"
href="file:///C:/SomeFolder/MyLib.dll" />
<!-- alternate…
<codeBase version="2.0.0.0"
href="http://www.SomeSite.com/AssembliesMyLib.dll" />
-->
closing tags… - Note: you want to use
<codeBase>
very judiciously- "If you place assemblies at random locations on your development machine, you are in effect re-creating the system registry (and the related DLL hell)…"(Troelsen: 5th Ed., p. 577)
- This is because moving or renaming the the parent folders will cause your client app to catch on fire
System.Configuration
namespace- You can add custom information to your
app.config
file System.Configuration
namespace provides types which allow you to read that data at runtime- Setting up the
app.config
file- Entries go inside
<appSettings>
element - Inside
<appSettings>
employ empty<add>
elements <add>
elements take the following 2 attributes:key
value
- Syntax/example:
<configuration>
<appSettings>
<add key="MyConstant" value="3.14159" />
<add key="MyGf" value="Denise" />
closing tags…
- Entries go inside
- To use in code
- Use
GetValue()
method of aAppSettingsReader
type- 1st parameter: name of key
- 2nd parameter: underlying type of key (obtained via
typeof()
operator)
- Syntax/example:
using System.Configuration;
namespace MyNs
{
public class MyClass
{
public void MyAppSettingReaderMethod()
{
AppSettingsReader ar = new AppSettingsReader();
double piage = (double)ar.GetValue("MyConstant", typeof(double));
string gf = (string)ar.GetValue("MyGf", typeof(string));
// Do something with values…
}
}
}
- Use
- You can add custom information to your
- Defining custom namespaces
- Type Reflection, Late Binding, & Attribute-Based Programming
- General
- Namespace:
System.Reflection
- Tools
ildasm.exe
&reflector.exe
- Namespace:
- Necessity of type metadata (& a working example)
- General
- .NET very good about supplying metadata on types (classes, enums, etc.)
- A number of .NET technologies rely on ability to discover this kind of metadata at runtime
- Ability to obtain this kind of metada is not unique to .NET
- Again, from within
ildasm.exe
utility you use to look at manifests of .NET assemblies- Every type within a .NET assembly identified with a
TypeDef #n
token (e.g.,TypeDef #1
) TypeRef #n
token is used for types defined in other assemblies- Note: the numbers assigned to
TypeDef #n
&TypeRef #n
only signify the order in which the compiler processed the file
- Every type within a .NET assembly identified with a
- Othen tokens & flags used in the metadata:
TypeDefName
- fully-qualified nameExtends
- fully-qualified name of base type
- Metadata for an enumeration
- Will have a line which roughly will read as:
Extends : [TypeRef] System.Enum
- Each individual enumeration will be identified with a
Field Name
token
- Will have a line which roughly will read as:
- Metadata for a class
- Class itself will be adorned with any number of flags indicating how the class is constructed, e.g.:
[Public]
[Class]
[Abstract]
- Method definitions include details on:
MethodName
ReturnType
Arguments
&Parameters
- Properties
- Note: with automatic properties you will see…
- A private backing field (if your automatic field is
FirstNm
you will see something likeField Name: <FirstNm>k__BackingField
) - For a read/write property 2 compiler-generated methods, such as:
get_FirstNm
set_FirstNm
- Lastly, the property definition itself, which will encapsulate
Getter
&Setter
tokens
- A private backing field (if your automatic field is
- Class itself will be adorned with any number of flags indicating how the class is constructed, e.g.:
TypeRef
blocks- There will be one for every externally-referenced type
- Note: Any time you wind up with an
Extends
token in your metadata you will have a correspondingTypeRef
block - Example:
- You define a custom enumeration in your assembly (as noted above, the metadata block for which will note that this extends
System.Enum
) - Your manifest will include a
TypeRef
block, which will include roughlyTypeRefName: System.Enum
- You define a custom enumeration in your assembly (as noted above, the metadata block for which will note that this extends
- Documenting
- The defining assembly
- Defined with
Assembly
block - Contains lots of info, including tokens for:
Name
Public Key
(if appropriate)Major Version, Minor Version,
etc.
- Defined with
- Referenced assemblies
- There is an
AssemblyRef #n
token for each external assembly - At a minimum this block will specify:
Name
(fully-qualified name)Version
- There is an
- String literals
- Each & every one in the assembly is documented in a
User Strings
block - Note: this means you should never store sensitive information as a string literal, as that data can be so easily accessed using
ildasm.exe
(among other methods)
- Each & every one in the assembly is documented in a
- The defining assembly
- General
- Reflection
- Understanding reflection
- General
- Reflection is defined as "the process of runtime type discovery" (Troelsen: 5th Ed., p. 586)
- In other words, reflection is the process of obtaining via code anything you can see via
ildasm.exe
- Types within the
System.Reflection
namespace (located inmscorlib.dll
)Assembly
(abstract class)AssemblyName
- used for getting details such as- Version
- Culture
MemberInfo
- Abstract class
- Base class for following abstract classes:
EventInfo
FieldInfo
MethodInfo
PropertyInfo
Module
(abstract class)ParameterInfo
- Not abstract
- Does not derive from
MethodInfo
- The
System.Type
class- Essential for reflection
System.Type
class is essentially giving you the metadata description of a type- Many methods of the class return types from the
System.Reflection
namespace - Properties (all boolean, obviously…)
IsAbstract
IsClass
IsEnum
IsInterface
- others…
- "Get" methods
- Examples:
GetEvents()
GetFields()
GetMembers()
GetMethods()
GetProperties()
- others…
- Notes:
- These types return arrays of "Info" types - i.e.,
GetMethods()
returns an array ofMethodInfo
types, etc. - There are also 'singular' versions of these methods - e.g.,
GetMethod()
will return aMethodInfo
type when passed the name of the method - Most of the overloaded methods allow you to pass in a
BindingFlags
enumeration - allows you to return, say, only static members
- These types return arrays of "Info" types - i.e.,
- Examples:
- Other methods
FindMembers()
- returns an array ofMethodInfo
types based on search criteriaGetType()
- Static method
- Returns a
Type
instance given a string
InvokeMember()
- used for late binding
- Obtaining a reference using
System.Object.GetType()
Type
is an abstract class (ergo, you cannotnew
an instance)- If you have design/compile-time knowledge of your types you can then implement code such as:
Student s = new Student();
Type t = s.GetType();
- Obtaining a reference using
typeof()
- Useful when you do not have, or want to create, an instance of a type
- Syntax/example:
Type t = typeof(Student);
- Notes:
typeof()
expects a strongly-typed name of the type as its parameter- Ergo, this is useful only when you have compile-time knowledge of the type
- Obtaining a reference using
System.Type.GetType()
- This is a static method
- This method takes (at a minimum) the fully-qualified string name of your type
- Ergo, you do not need compile-time knowledge of the
type
- Method has been overloaded to take 2 boolean parameters:
- 1st parameter: specifies whether an exception should be thrown if the type cannot be found
- 2nd parameter: specifies case sensitivity of the string parameter
- Syntax/example:
Type t = Type.GetType("MyNs.Student", false, true);
+
token- Used to denote a nested type
- Example:
- Within your student class you have a
Status
enumeration to specify full-time, part-time, etc. - Syntax:
Type t = Type.GetType("MyNs.Student+Status");
- Within your student class you have a
- General
- Code examples
- Reflecting on methods:
static void ListMethods(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
Console.WriteLine("-> {0}", m.Name);
} - Reflecting on field names (using LINQ):
static void ListFields(Type t)
{
var fieldNames = from f in t.GetFields() select f.Name;
foreach (var name in fieldNames)
Console.WriteLine("-> {0}", name);
} - Returning interfaces
- Code here must be slightly different because an interface is a .NET type unto itself
- Syntax/example:
static void ListInterfaces(Type t)
{
var ifaces = from i in t.GetInterfaces() select i;
foreach (Type i in ifaces)
Console.WriteLine("-> {0}", i.Name);
}
- Using misc. properties & methods:
static void ListVariousStats(Type t)
{
Console.WriteLine("Base class is: {0}", t.BaseType);
Console.WriteLine("Is type abstract? {0}", t.IsAbstract);
Console.WriteLine("Is type generic? {0}", t.IsGenericTypeDefinition);
} - Again, to generate a
Type
use code such as:
Type t = Type.GetType(someTypeName);
- Reflecting on generic types
- Must make use of the back tick character - i.e.,
`
- Following the back tick character you include a number, which is the number of type parameters the type supports.
- Example:
List<T>
has (obviously) only 1 type parameter, so to reflect overSystem.Collections.Generic.List<T>
the string you use is as follows…
Type t = Type.GetType("System.Collections.Generic.List`1");
- Example:
Dictionary<TKey, TValue>
has 2 type parameters, so to reflect over it the string you use is as follows…
Type t = Type.GetType("System.Collections.Generic.Dictionary`2");
- Must make use of the back tick character - i.e.,
- Reflecting on method parameters & return values
- You work with
MethodInfo
type for this - In particular,
MethodInfo
itself contains:ReturnType
propertyGetParameters()
method
- Getting
ReturnType
info:
static void GetReturnType(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
Console.WriteLine("{0} returns {1}", m.Name, m.ReturnType.FullName);
} - Getting parameter information:
static void ShowMethodParams(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
{
string paramInfo = "( ";
// Get params…
foreach (ParameterInfo pi in m.GetParameters())
{
paramInfo += string.Format("{0} {1}", pi.ParameterType, pi.Name);
}
paramInfo += " )";
Console.WriteLine("{0} {1}", retVal, m.Name, paramInfo);
}
} - Leaning on
ToString()
- Has been overridden on the
MethodInfo, PropertyInfo
, etc. types - Displays the signature of the requested item
- The more compact approach:
static void ListMethodsUsingToString(Type t)
{
var methodNames = from n in t.GetMethods() select n;
foreach (var name in methodNames)
Console.WriteLine("->{0}", name);
}
- Has been overridden on the
- You work with
- Reflecting on methods:
- Dynamically loading assemblies
- While the above is all well & good, it only allow us to handle assemblies referenced at compile time
- Dynamic loading is "the act of loading external assemblies on demand" (Troelsen: 5th Ed., p. 596)
- You use the
Assembly
(abstract) class- Primary methods of interest:
Load()
- requires only friendly name of assemblyLoadFrom()
- You supply full name (i.e., path & extension) of assembly of interest
- Can also read from
<codeBase>
element from yourapp.config
file to retreive path
- Both methods return a
System.Reflection.Assembly
type - These methods allow you to accomplish what you would otherwise rely on client
app.config
file to obtain - A smart developer wraps these methods in
try/catch
blocks in case the searched-for assembly is not found - Syntax/example (of how to obtain info about an assembly):
static void DisplayTypesInAsm(Assembly asm)
{
Console.WriteLine("->{0}", asm.FullName);
Type[] types = asm.GetTypes();
foreach (Type t in types)
Console.WriteLine("Type: {0}", t);
Console.ReadLine();
} - The
Assembly.GetName()
method- Does not return a
string
- Instead returns an
AssemblyName
type- This type has a number of methods & properties
- Commonly used properties
FullName
Name
Version
- Does not return a
- Primary methods of interest:
- Reflecting on shared assemblies
Assembly.Load()
has been overloaded multiple times- Can pass in a number of parameters, such as
- Version number format: major.minor.build.revision
- Public key token
- Display name
- All of the items which collectively identify an assembly
- Format
- Comma-delimited name/value pairs
- Begins with friendly name of the assembly
- Optional qualifiers follow in parentheses
- Example:
MyLibr (,Version = 2.1.7.2) (,Culture = English) (,PublicKeyToken=blahblahblah)
- Notes:
- When specifying the
PublicKeyToken
value omit all embedded spaces found inildasm.exe
PublicKeyToken=null
indicates you must bind against a non-strongly named assemblyCulture=""
means you use the default culture of target machine- Other than friendly name, order of parameters is immaterial
- When specifying the
- Syntax/example (loading a shared assembly):
Assembly.Load(@"MyLibrary, Version=1.0.0.3,
emsp;PublicKeyToken="null", Culture="""); - Generally, though, it is much easier to avail yourself of the
AssemblyName
type- "Typically, this class is used in conjuntion with
System.Version
, which is an OO wrapper around an assembly's version number." (Troelsen: 5th Ed., p. 599) - An
AssemblyName
instance can then be passed intoAssembly.Load()
- Syntax/example:
// Use AssemblyName to define display name…
AssemblyName asName = new AssemblyName();
asName.Name = "MyLibrary";
Version v = new Version("1.1.2.0");
asName.Version = v;
Assembly a = Assembly.Load(asName); - Note: when working with this myself I had trouble with
CultureInfo
property- You need to new a
CultureInfo
type - e.g.,myAsmName.CultureInfo = new CultureInfo("en-US");
- MSDN has no guidance for leaving this blank
- You need to new a
- Passing in a public key token
- There is no corresponding property
- Most appropriate property is the
KeyPair
property, which requires you to point to a*.snk
file
- "Typically, this class is used in conjuntion with
- Syntax/example (obtaining desired info on loaded assembly):
private static void DisplayInfo(Assembly a)
{
Console.WriteLine("Loaded from GAC? {0}", a.GlobalAssemblyCache);
// Note uses of GetName()…
Console.WriteLine("Asm Name: {0}", a.GetName().Name);
Console.WriteLine("Asm Version: {0}", a.GetName().Version);
Console.WriteLine("Asm Culture: {0}",
a.GetName().CultureInfo.DisplayName);
}
- Understanding reflection
- Late binding
- General
- Late binding is creating an instance of a type (in order to invoke its members) at runtime
- Typically means you do not have compile-time knowledge of type's existence
- Implications
- You have not set a reference to the type in your project
- Caller's manifest has no reference to the assembly
- Major rationale for late binding is to allow you to build extendable apps
- Basic steps
- Define an
Assembly
instance, representing your assembly of instance - Invoke
GetType()
on your assembly, to obtain the type (from within the assembly) in which you are interested - Pass that
Type
intoActivator.CreateInstance()
to get an instance - Put reflection to work on the returned
System.Object
- Define an
- The
System.Activator
class- The base type used for late binding
- Defined in
mscorlib.dll
CreateInstance()
is main method used for late binding- Method is oveloaded, but easiest version takes a
Type
object - Limitation with late binding
Activator.CreateInstance()
does not return a strongly-typed instance- Instead, it returns a
System.Object
- Syntax/example (using Troelsen's
Car
example):
static void Main(string[] args)
{
// Try to load local copy of Car library…
Assembly a = null;
try
{
a = Assembly.Load("CarLibrary");
}
catch(FileNotFoundException ex)
{
Console.WriteLine(ex.Message);
return;
}
if (a != null)
CreateUsingLateBinding(a);
}
static void CreateUsingLateBinding(Assembly asm)
{
// Try-catch blocks omitted…
// Get metadata for the MiniVan type…
Type miniVan = asm.GetType("CarLibrary.Minivan");
// Create the minivan on the fly…
object obj = Activator.CreateInstance(miniVan);
} - You then use reflection to obtain & invoke the methods of your returned
System.Object
- Invoking methods with no parameters
- To get a hold of a method from your returned
System.Object
- Use
Type.GetMethod()
, which returns aMethodInfo
instance - Invoke
Invoke()
(!) on thatMethodInfo
instance
- Use
MethodInfo.Invoke()
takes 2 parameters- The object on which to invoke the method (of type
System.Object
) - An array of
System.Object
s, which serve as the method's parameters
- The object on which to invoke the method (of type
- If the method takes no parameters simply pass in
null
as the 2nd parameter toMethodInfo.Invoke()
- Syntax/example (again using Troelsen's
Car
example)
// Same as above…
Type miniVan = asm.GetType("CarLibrary.Minivan");
object obj = Activator.CreateInstance(miniVan);
// Get info for TurboBoost…
MethodInfo mi = miniVan.GetMethod("TurboBoost");
// Invoke method (TurboBoost takes no parameters)…
mi.Invoke(obj, null);
- To get a hold of a method from your returned
- Invoking methods with parameters
- Again leaning on Troelsen's example (Troelsen: 5th Ed., pp. 603-4), the
TurnOnRadio()
method takes 2 params:- A
bool
controlling whether to turn the radio on or off - An
enum
controlling whether to use radio, CD, tape, etc.Enums
, of course, have underlying numerical values- Therefore, you can pass in a numerical value as your parameter rather than pass in an
enum
- A
- Syntax/example (omitting try/catch blocks):
static void InvokeMethodsWithArgsUsingLateBinding(Assembly asm)
{
// Get metadata description of sports car…
Type sport = asm.GetType("CarLibrary.SportsCar");
// Create the sports car…
Object obj = Activator.CreateInstance(sport);
// Invoke TurnOnRadio with args…
MethodInfo mi = sport.GetMethod("TurnOnRadio");
mi.Invoke(obj, new object[] { true, 2 });
}
- Again leaning on Troelsen's example (Troelsen: 5th Ed., pp. 603-4), the
- General
- Attributes
- .NET attributes
- General
- Attributes are a way to embed additional information in assembly metadata
- Essentially nothing more than code annotations
- Concept is not unique (or new) to .NET
- Can be applied to any
- Module
- Assembly
- Type (class, interface, etc.)
- Type member
- .NET attributes extend
System.Attribute
base class - Inherit from this class to build your own, custom attributes
- Sampling of predefined attributes
[CLSCompliant]
- tells compiler to check for (& thus enforce) CLS conformity of item attributed[DllImport]
- "Allows .NET code to make calls to any unmanaged C- or C++-based code library" (Troelsen: 5th Ed., p. 605)
- This includes calls to underlying operating system
- Do not use to communicate with COM-based code
[Obsolete]
- generates a compiler warning to any user/consumer of the attributed item[Serializable]
- Usable on classes & structures
- Means that the item perists its current state into a stream
[Nonserialized]
- i.e., do not persist item's current state into a stream[WebMethod]
- Used to flag a method as being invokable via an HTTP request
- Also tells CLR to serialize any method return values as XML
- Attribute consumers
- Until & unless some software reflects over the information in attributes that information is simply harmless excess entries in your compiled assembly
- CLR compiler itself looks for attributes such as
[CLSCompliant]
[Obsolete]
- Particular methods in various .NET classes also look for individual attributes
- Can create custom methods which look for custom attributes
- Applying attributes in C#
- Attributes show up in compiled metadata
- Can apply multiple attributes to a type
- Syntax/example - can put (comma-separated) multiple attributes with single set of square brackets:
[Serializable, Obsolete([Are you kidding?")]
public class Pager
{ // Implementation code… } - Syntax/example - can 'stack' multiple:
[Serializable]
[Obsolete([Are you kidding?")]
public class Pager
{ // Implementation code… } - Note: either syntax achieves exactly the same results
- Syntax/example - can put (comma-separated) multiple attributes with single set of square brackets:
- C# shorthand notation
- All .NET attributes carry a suffix of
Attribute
- In other words, the actual name of the
Obsolete
attribute isObsoleteAttribute
- C# allows you to omit that
Attribute
suffix - Notes:
- Any custom attributes you create should also carry the
Attribute
suffix - Not all .NET languages allow you to omit the
Attribute
suffix when applying an attribute
- Any custom attributes you create should also carry the
- All .NET attributes carry a suffix of
- Specifying constructor parameters for attributes
- As already seen with
[Obsolete]
attributes can take constructor parameters - In fact, the
[Obsolete]
attribute has 3 constructors - As with any custom class you can supply any number of constructors to your custom attributes
- When you do create parameterized constructors for custom attributes
- Your attribute is not allocated into memory until the parameters are targeted via reflection from
- Another type
- Some external tool
- Instead, "the string data defined at the attribute level is simply stored within the assembly as a blurb of metadata" (Troelsen: 5th Ed., p. 608)
- Your attribute is not allocated into memory until the parameters are targeted via reflection from
- As already seen with
- The
Obsolete
attribute- The string passed into the constructor shows up as a warning in the error window upon compiling a class so attributed
- The message also shows up when you hover your cursor over any instantiated instance of the class
- General
- Building custom attributes
- General
- Again, derive from
System.Attribute
- .NET Best Practice: define all custom attribute classes as
sealed
- Syntax/example:
public sealed class SomeCustomFeatureAttribute : System.Attribute
{
public string Description { get; set; }
// Ctors…
public SomeCustomFeatureAttribute(string descrip)
{ Description = descrip; }
public SomeCustomFeatureAttribute() { }
} - Custom attributes are applied to classes, methods, etc., just as you do with built-in .NET attributes
- Again, derive from
- Named property syntax
- So far have passed values into attributes simply via their constructors
- With named property syntax can also pass in values via any public, writable properties
- Syntax/example:
[Serializable]
[SomeCustomFeature(Description = "My little class")]
public class MyClass
{
}
- Restricting attribute usage
- As it stands custom attributes you create can be applied to any part of your code - classes, methods, enums, etc.
- This, however, may not be the range of usage you want to allow
- Solution: apply
[AttributeUsage]
attribute to your custom attribute- Defining class contains an
enum
calledAttribute Targets
xx
< xx >
- Values:
All
Assembly
Class
Constructor
Field
Method
- Others
- Use the pipe symbol (
¦
) to combine enum values on an "or" basis
- Values:
- Attribute also has a couple of boolean named properties
AllowMultiple
- specifies whether the attribute can apply to the same item more than once (how the hell would that work???)Inherited
- whether attributes apply to derived classes
- Syntax/example:
// Apply AttributeUsage to a custom attribute…
[AttributeUsage(AttributeTargets.Class ¦ AttributeTargets.Struct,
Inherited = true[
public sealed class MyDescriptionAttribute : System.Attribute
{ // Implementation code… }
- Defining class contains an
- General
- Assembly-level (and module-level) attributes
- General
- These attributes apply to all types within a targeted assembly (or module)
- Accomplish this via use of the 'tags'
- [module:]
- [assembly:]
- Troelsen strongly encourages use of
CLSCompliant
attribute in this manner, especially if there is any change of your assembly being consumed by any types written in other .NET languages (Troelsen: 5th Ed., pp. 612-3) - Syntax/example:
// Place 'using' statements first…
using System;
using System.Collections.Generic;
// etc. …
// Next place assembly- or module-level attributes…
[assembly: CLSCompliant(true)]
// Namespace and types are next…
namespace MyNS
{
// Etc., etc., etc. …
}
- The VS 2010
AssemblyInfo.cs
file- By default, VS creates a file called
AssemblyInfo.cs
for all new projects - Access file via
- Opltimal place for assembly-level attributes
- May well find attributes already there (values of which are, then, already in the assembly-level metadata):
[AssemblyCompany]
[AssemblyCopyright]
[AssemblyDescription]
[AssemblyVersion]
- others…
- By default, VS creates a file called
- General
- Reflecting on attributes
- Using early binding
- First, nothing can happen with a custom attribute unless a client app or type has compile-time knowledge of that attribute
- Thus, you need to set a reference to your custom attribute class in your client app
- Next
- Get a
type
reference to your type (usually class) of interest - Invoke
Type.GetCustomAttributes()
on thattype
instance- The method takes a boolean parameter, which controls whether to return custom attributes up the type's inheritance chaing
- The method returns an array of
object
s
- Get a
- Syntax/example (directly from Troelsen: 5th Ed., pp. 614-5):
// Include standard 'using' statements, then add…
using AttributedCarLibrary;
namespace VehicleDescriptionAttributeReader
{
class Program
{
static void Main(string[] args)
{
ReflectOnAttributesUsingEarlyBinding();
Console.ReadLine();
}
private static void ReflectOnAttributesUsingEarlyBinding()
{
// Get a type representing the Winnebago…
Type t = typeof(Winnebago);
// Get all attributes of the Winnebago…
object[] customAtts = t.GetCustomAttributes(false);
// Print the description…
foreach (VehicleDescriptionAttribute v in customAtts)
Console.WriteLine("-> {0}\n", v.Description);
}
}
}
- Using late binding
- General approach
- Creating an
Assembly
instance - Getting
Type
references- The class defining the custom attribute
- An array of
Types
, by invokingGetTypes()
on theAssembly
instance
- Assuming you have defined a public property in your custom attribute, get a
PropertyInfo
instance to capture that type - Lastly, invoke
GetValue()
on that property (passing in the appropriate parameters) to hit the property' accessor
- Creating an
- Syntax/example (from Troelsen: 5th Ed., p. 616, try/catch blocks omitted):
private static void RefelctAttributesUsingLateBinding()
{
// Load the local copy of AttributedCarLibrary…
Assembly asm = Assembly.Load("AttributedCarLibrary");
// Get type info of the VehicleDescriptionAttribute…
Type vehicleDesc =
asm.GetType("AttributedCarLibrary
.VehicleDescriptionAttribute");
// Get type info of the Description property…
PropertyInfo propDesc = vehicleDesc.GetProperty("Description");
// Get all types in the assembly…
Type[] types = asm.GetTypes();
// Iterate over each type & obtain any VehicleDescriptionAttributes…
foreach (Type t in types)
{
object[] objs = t.GetCustomAttributes(vehicleDesc, false);
// Iterate over each VehicleDescriptionAttribute and print
// the description using late binding…
foreach (object o in objs)
{
Console.WriteLine("-> {0}: {1}\n",
t.Name, propDesc.GetValue(o,null));
}
}
}
- General approach
- Using early binding
- .NET attributes
- Building an extendable application
- General
- This is where you build your app so that other assemblies can be referenced that will add functionality
- Those added-in assemblies are called snap-ins
- See Troelsen: 5th Ed., pp. 618-24, for full example
- Building
CommonSnappableTypes.dll
- Snap-ins only work if you provide an interface for them to adhere to
- While conceivably this could be an abstract class, Troelsen's example uses an interface
- Interface is declared in this external assembly
- The assembly also defines an attribute (i.e., a class inheriting from
System.Attribute
- The attribute is to be used by any class that wants to be snapped-in
- Building the snap-ins
- Troelsen's example has 2 assemblies, 1 written in C# & 1 in VB.Net
- Each snap-in contains a single class, which
- Implements the above-mentioned interface
- Employs the custom attribute
- As such, each assembly contains a reference to the CommonSnappableTypes dll
- Note re: implementing the interface
- Troelsen elects to implement the interface explicitly
- Logic is that the snapped-into app is the only thing which you really want to interact with the methods, etc. defined by the interface
- Implicit implementation would more readily expose the methods, etc., defined by the interface
- Building an extendable Windows Form application
- Assembly references
- You must include a reference to your
CommonSnappableTypes
-like assembly - In practice, however, you do not have design or compile-time knowledge of the snap-ins
- You must include a reference to your
- Snapping in the snap-ins
- Troelsen's example generates a
OpenFileDialog
instance & has user navigate to snap-in dll - Once the user opens the file you then must write code which
- Loads the assembly into memory, via
Assembly.LoadFrom()
- Looks for any & all classes implementing the interface(s) defined in the CommonSnappableTypes-like assembly
- Use LINQ for this, working on the just-created
Assembly
instance - Syntax/example:
// Get all IAppFunctionality compatible classes in assembly…
var theClassTypes = from t in theSnapInAsm.GetTypes()
where t.IsClass &&
(t.GetInterface("IAppFunctionality") != null)
select t;
- Use LINQ for this, working on the just-created
- Uses late binding to create the desired type, &, as necessary, invoke methods
- Use
foreach
to loop through the just-created assembly, invoking desired methods - Syntax/example:
// Now, create the object and call DoIt() method…
foreach (Type t in theClassTypes)
{
// Use late binding to create the type…
IAppFunctionality itfApp =
(IAppFunctionality)theSnapInAsm.CreateInstance(t.FullName, true);
itfApp.DoIt();
}
- Use
- Loads the assembly into memory, via
- Lastly, you can also handle custom attributes
- Within the just-described
foreach
loop you can pass the type to a helper method - Syntax/example:
private void DisplayCompayData(Type t)
{
// Get [CompanyInfo] data…
var compInfo = from ci in t.GetCustomAttributes(false)
where
(ci.GetType() == typeof(CompanyInfoAttribute))
select ci;
// Show data…
foreach (CompanyInfoAttribute c in compInfo)
{ // Do something with the custom attribute… }
}
- Within the just-described
- Troelsen's example generates a
- Assembly references
- General
- General
- Processes, AppDomains, and Object Contexts
- General
- Application domains, aka AppDomains, "are logical subdivisions within a given process that host a set of related .NET assemblies" (Troelsen: 5th Ed., p. 625)
- Contextual boundaries
- Subdivisions of AppDomains
- Used to group similar .NET objects
- Allow CLR to segregate, & handle appropriately, objects with special runtime requirements
- The role of a Windows process
- General
- Process is a Windows concept, not a .NET one
- Essentially means a running program
- More precisely, though, refers to all items used by a running program
- The resources used by a program, e.g.
- Code libraries
- The primary thread
- Memory allocations
- The resources used by a program, e.g.
- Windows creates a distinct process for every
*.exe
loaded into memory - Advantages of processes
- By segregating apps into processes a failure in one process does not affect others
- Processes are thus safe, bounded areas in which to run apps
- Characteristics
- Data in one process cannot be shared (&, therefore, not corrupted) by another process
- Exception: you make use of a distributed computing API, such as (of course) WCF
- Every Windows process is assigned a unique Process Identifier (PID)
- Processes can be loaded & unloaded by
- Windows
- Programmatically
- You can view all running processes in Task Manager (note: PID not a default column in Task Manager - must be added)
- The role of threads
- "Every Windows process contains an initial 'thread' that functions as the entry point for the application." (Troelsen: 5th Ed., p. 626)
- Terminology
- The
Main()
method of any .NET app represents that app's entry point - Entry point always spawns (automatically) the app's primary thread
- The
- Single-thread processes
- Ipso facto thread safe
- Such processes can be sluggish, however
- Secondary threads
- Allowed by both the Windows API & .NET
- Also termed worker threads
- Created by primary thread
- Characteristics of all threads - i.e., both primary & secondary:
- Each one represents "a unique path of execution within the process" (Troelsen: 5th Ed., p. 627)
- Have "concurrent access to all shared points of data within the process" (Troelsen: 5th Ed., p. 627) [emphasis mine]
- Multi-threaded apps
- Created to improve overall app performance
- Classic case is to create a secondary thread to access a database while primary thread allows user to continue interacting with GUI
- Note: adding worker threads does not always improve performance, as it takes time for the CPU to switch between threads
- Further, some machines (i.e., CPUs) are not capable of genuine multi-threading
- In this case the OS must essentially 'fake it'
- All threads have a priority level
- CPU will execute a thread, based on its priority level, for some unit of time (called a time slice)
- CPU then suspends that execution & moves to another thread
- To accommodate all this, each thread
- Can write to Thread Local Storage (TLS), which keeps track of the status of the thread whenever the CPU suspends working on that thread
- Is also given its own call stack
- General
- Interacting with processes under the .NET platform
- General
- The main namespace of interest is
System.Diagnostics
- Types within allow programmatic interaction with
- Processes
- Diagnostic-related types, such as
- The system even log
- Performance counters
- Primary types of
System.Diagnostics
Process
- Gives you access to both local & remote processes
- Allows you to start & stop processes programmatically
Module
(&ProcessModuleCollection
)- Represents the
*.dll
or*.exe
loaded into a process - A
ProcessModule
does not, by any means, have to be .NET
- Represents the
ProcessStartInfo
- a set of values passed into theProcess.Start()
methodProcessThread
(&ProcessThreadCollection
)- Used to represent/capture a thread within a given process
- Note: this type is used for diagnostics, not to create new threads
System.Diagnostics.Process
in-depth- Primary properties
ExitTime & StartTime
- 'Date-stamped' properties
- Of type
DateTime
Handle
- An
IntPtr
assigned by the OS - Useful when interacting with unmanaged code
- An
ID
- a process's PIDMachineName
MainWindowTitle
- Caption of the main window of the controlling app
- Will equal
null
if the app has no main window
ProcessName
- i.e., the name of the app itselfResponding
- use to check for hung appsThreads
- of typeProcessThreadsCollection
- Primary methods
CloseMainWindow()
GetCurrentProcess()
- Static method
- Returns a
Process
representing the currently active process
GetProcesses()
- Static method
- Returns an array of
Process
objects
GetProcessById()
- Static method
- Takes an
int
(the PID) as a parameter - A wise developer wraps invocation of this method in a
try
block - If you pass in an invalid PID you will throw an
ArgumentException
Kill()
Start()
- Returns a
Process
- If unsuccesful throws an
InvalidOperationException
- Returns a
- Primary properties
- The main namespace of interest is
- Enumerating running processes
- Syntax/example (sorting processes by PID):
var runningProcs =
from proc in Process.GetProcesses(".")
orderby proc.Id
select proc; - Note: using dot notation - i.e.,
Process.GetProcesses(".")
- points you to processes on the local machine
- Syntax/example (sorting processes by PID):
- Investigating a process's thread set
- Obviously, you first need a
Process
instance (e.g., viaGetProcessById()
) - Then add code such as
ProcessThreadCollection myThreads = someProcess.Threads;
ProcessThread
propertiesCurrentPriority
(Gets)Id
(Gets)IdealProcessor
(Sets)PriorityLevel
(Gets/Sets)ProcessorAffinity
(Sets - the processors on which the thread can run)StartAddress
(Gets - memory address of the function that fired up the thread)StartTime
(Gets)ThreadState
(Gets)TotalProcessorTime
(Gets)WaitReason
(Gets)
- Note re
ProcessThread
:- Not the type you use to create, suspend, or kill threads
- Use for diagnostics, & some thread manipulation
- Obviously, you first need a
- Investigating a process's module set
- As 'always' must first identify a
Process
- Once that is accompished
- Instantiate a
ProcessModuleCollection
instance from theSystem.Diagnostics.Process.Modules
property - Iterate over each
ProcessModule
within that collection
- Instantiate a
- Syntax/example (
Try/Catch
blocks ommitted):
// Instantiate a Process instance…
Process theProc = null;
theProc = Process.GetProcessById(pID);
ProcessModuleCollection theMods = theProc.Modules;
foreach (ProcessModule pm in theMods)
{ // Do stuff… }
- As 'always' must first identify a
- Starting & stopping processes programmatically
Process.Start()
- Has been overloaded a few times
- As you would expect, must at least provide name of app you want to launch
Process.Kill()
- Invoke this on the instance of your
Process
- Particularly important to wrap this in a try/catch block, to handle the always-possible scenario that the user will have terminated the program himself
- Invoke this on the instance of your
- Syntax/example (try/catch blocks omitted):
static void StartAndKillProcess()
{
Process ieProc = null;
// Launch IE and go to Facebook…
ieProc = Process.Start("IExplorer.exe", "www.facebook.com");
Console.Write("--> Hite ENTER to kill {0}", ieProc.ProcessName);
Console.ReadLine();
ieProc.Kill();
}
- Controlling process startup using the
ProcessStartInfo
class- This type can be passed as a parameter into
Process.Start()
- There are about 30 properties that one can set on a
ProcessStartInfo
instance - Check SDK documentation for details
- This type can be passed as a parameter into
- General
- Understanding .NET application domains
- General
- Unmanaged executables are hosted by Windows processes
- .NET apps, however, are hosted by a partition within a process
- As noted above, that partition is called an application domain
- Benefits of app domains
- Makes .NET OS-neutral, as app domains introduce a layer of abstraction between OS & .NET apps
- Less memory intensive than a full process
- CLR can load & unload app domains much more quickly than it can Windows processes
- This also leads to big improvements in server apps
- Better isolation of a loaded process
- Failure in one app domain leaves other app domains unaffected
- Also means you can only access data across app domains via distributed progamming protocols (e.g., WCF)
- A single process can host multiple app domains
- That, however, is atypical (i.e., generally a 1-to-1 relationship between a process & an app domain)
- A process does host the default application domain
- Created by the CLR when the process launches
- CLR can create additional app domains within a process as required
- The
System.AppDomain
class- Located in good ole'
mscorlib.dll
- Primary methods
CreateDomain()
- creates within current processCreateInstance()
- Loads an external assembly into app domain
- Then creates an instance of a type found in once-yon assembly
ExecuteAssembly()
- Fires up an
*.exe
assembly - Parameter is (obv) name of that assembly/file
- Fires up an
GetAssemblies()
- Returns all of the .NET assemblies loaded into current app domain
- Unmanaged assemblies are ignored
GetCurrentThreadId()
- static methodIsDefaultAppDomain()
- Takes no arguments
- Returns a
bool
Load()
- Loads an assembly into app domain
- Pass in the assembly's friendly name
Unload()
- Static method
- Note re: unloading assemblies
- Cannot unload individual assemblies
- Can only unload an app domain, which then unloads all associated assemblies
- Primary properties
BaseDirectory
- "Gets the directory path that the assembly resolver uses to probe for assemblies." (Troelsen: 5th Ed., p. 639)
- For the default app domain this will give you the path of the executable running in the domain
CurrentDomain
- Static property
- Gets the app domain for the currently executing thread
FriendlyName
- Gets
- Will always be the name of the executable the domain is hosting
MonitoringIsEnabled
(Gets/Sets)- Apparently, you can monitor the CPU & memory of app domains within the current process
- Note: monitoring can only be enabled (i.e., once enabled it cannot be disabled)
SetUpInformation
- Gets confirguration details for an app domain
- Of type
AppDomainSetup
- Primary events
AssemblyLoad
AssemblyResolve
- fires "when the assembly resolver cannot find the location of a required assembly" (Troelsen: 5th Ed., p. 640)DomainUnload
- fires when an app domain is about to be unloadedFirstChanceException
- Fires whenever an exception is thrown from within the app domain
- Allows you to catch exceptions before 'normal' catch blocks handle their exceptions
ProcessExit
- Only fires from the default app domain
- Signals that the default app domain's parent process is exiting
UnhandledException
- the shame of all self-respecting developers
- Located in good ole'
- General
- Interacting with the default application domain
- General
- Again, you never have to fire up the default app domain - CLR most graciously does this for you
- You latch onto the default app domain via the static
AppDomain.CurrentDomain
property
- Enumerating loaded assemblies
AppDomain.GetAssemblies()
returns an array ofAssembly
objectsAssembly
objects are inSystem.Reflection
namespace, so you will need to add appropriateusing
statement- Note: as your code changes the assemblies loaded into your default app domain may very well change
- Receiving assembly load notifications
- You work with the
AssemblyLoad
event - This event works with the
AssemblyLoadEventHandler
delegate AssemblyLoadEventHandler
delegate takes 2 parameters- 1st:
System.Object
- 2nd:
AssemblyLoadEventArgs
- 1st:
- Syntax/example:
AppDomain defaultAD = AppDomain.CurrentDomain;
defaultAD.AssemblyLoad += (o,s) =>
{
Console.WriteLine("{0} has been loaded!",
s.LoadedAssembly.GetName().Name);
};
- You work with the
- General
- Creating new application domains
- General
- Generally will not create app domains in .NET programming
- 2 cases where it may/will arise
- Must do so when you create dynamic assemblies
- May have to do so with certain .NET security APIs
AppDomain.CreateDomain()
method- Overloaded several times
- Will always have to supply at least a new friendly name for the domain
- Interestingly, when you create a new app domain generally the only assembly loaded into that new domain is
mscorlib.dll
- Loading assemblies into custom application domains
- Use
AppDomain.Load()
method- Use
try/catch
blocks - Catch for
FileNotFoundException
, which is inSystem.IO
namespace
- Use
- Note:
ExecuteAssembly()
calls that assembly'sMain()
method
- Use
- Programmatically unloading
AppDomain
s- As noted above, again, you cannot unload individual assemblies
- You can only unload entire app domains
- Events of interest
AppDomain.DomainUnload
ProcessExit
- Fired when the default app domain is torn down
- This, of course, means the entire process is being terminated
- General
- Understanding object context boundaries
- General
- AppDomains can be further sub-divided
- These sub-divisions are called context boundaries
- Very rare that you have to get involved with manipulating context boundaries
- How context boundaries work
- Set up for objects with special runtime requirements
- Allows CLR to intercept "method invocations into and out of a given context" (Troelsen: 5th Ed., p. 648)
- Example:
- You annotate a class with a
[Synchronization]
attribute - This attribute tells CLR that the class requires automatic thread safety
- CLR will then place that class in its own, synchronized context when the calss is allocated
- You annotate a class with a
- Not surprisingly, every AppDomain has a default context
- The default context is referred to as context 0
- Any & all run-of-the-mill .NET objects (i.e., those not requiring a special context) go here
- Subsequent contexts are Context 1, Context 2, etc.
- Context-agile & context-bound types
- Context-agile objects
- Those not requiring any special contexts
- These objects can be accessed from throughout the hosting AppDomain
- Context-bound objects
- Obviously, these are the 'special needs' objects
- These must derive from
System.ContextBoundObject
base class - This base object enforces the requirement that the object can only be handled properly within its own, special context
- Further, context-sensitive types are flagged with a category of .NET attributes called context attributes
- Context attributes all derive from
ContextAttribute
base class
- Context-agile objects
- Defining a context-bound object
- Include
using System.Runtime.Remoting.Contexts;
statement - Using the
[Synchronization]
attribute ensures that a class will be loaded in a thread-safe context - Deriving the object's class from
ContextBoundObject
ensures that the object is never moved out of that context
- Include
- Inspecting an object's context
- You can get the current context off the current thread
- The
ContextProperties
property of a context is then of typeIContextProperty
- Syntax/example:
class MyClass
{
// Ctor…
public MyClass()
{
// Get context info & print out context ID upon instantiation…
Context ctx = Thread.CurrentContext;
Console.WriteLine("{0} object in context {1}",
this.ToString(), ctx.ContextID);
foreach (IContextProperty itfCtxProp in ctx.ContextProperties)
Console.WriteLine("-> Ctx Prop: {0}", itfCtxProp.Name);
}
}
- General
- General
- CIL & the Role of Dynamic Assemblies
- General
- Reasons for learning the grammar of CIL
- Examining CIL directives, attributes, & opcodes
- General
- CIL directives
- CIL attributes
- CIL opcodes
- CIL opcode/CIL mnemonic distinction
- Pushing & popping: the stack-based nature of CIL
- Round-trip engineering
- General
- CIL code labels
- Interacting with CIL: modifying an
*.il
file - Compiling CIL code using
ildasm.exe
- Authoring CIL code using SharpDevelop
peverify.exe
- CIL directives & attributes
- Specifying externally-referenced assemblies in CIL
- Defining the current assembly in CIL
- Defining namespaces in CIL
- Defining class types in CIL
- Defining & implementing interfaces in CIL
- Defining structures in CIL
- Defining enums in CIL
- Defining generics in CIL
- Compiling the
CILTypes.il
file
- .NET base class library, C#, & CIL data type mappings
- Defining type members in CIL
- Defining field data in CIL
- Defining type constructors in CIL
- Defining properties in CIL
- Defining member parameters
- Examining CIL opcodes
- General
- The
.maxstack
directive - Declaring local variables in CIL
- Mapping parameters to local variables in CIL
- The hidden
this
reference - Representing iteration constructs in CIL
- Building a .NET assembly with CIL
CILCars.dll
CILCarClient.exe
- Dynamic assemblies
- General
- The
System.Reflection.Emit
namespace - The
System.Reflection.Emit.ILGenerator
- Emitting a dynamic assembly
- Emitting the assembly & module set
- The
ModuleBuilder
type - Emitting the HelloClass type & the string member variable
- Emitting the constructors
- Emitting the
SayHello()
method - Using the dynamically-generated assembly
- Dynamic Types & the Dynamic Language Runtime
- The C#
dynamic
keyword- General
- Overview
- Introduced in .NET 4.0
- With its loose typing introduces scripting-like behavior to .NET
- This allows interaction with .NET-savy dynamic languages like IronRuby or IronPython
- The
dynamic
keyword- Enables you to use reflection to perform late-bound method calls
- Simplifies communication with COM libraries
- We, of course, already have the
var
keyword- Discussed in Implicitly typed local variables
- A
var
variable becomes strongly typed as soon as it has been assigned a value
System.Object
type- Could also use this to capture values which you do not know at design time
- A
System.Object
variable also becomes strongly typed as soon as it has been assigned a value - However, to gain access to its members you would first have to perform an explicit cast
- In some ways the
dynamic
keyword is a specialized version ofSystem.Object
- However, a
dynamic
variable is not strongly-typed- "Said another way, dynamic data is not statically typed." (Troelsen: 5th Ed., p. 703) [emphasis in the original]
- This means you can, say, initialize a dynamic variable as a
bool
but subsequently assign it astring
value - Note:
- You can do the same with a
System.Object
variable - There are, however, differences between a
System.Object
variable & adynamic
variable
- You can do the same with a
- Overview
- Calling members on dynamically-declared data
- Can do so via standard .NET dot notation (public members, of course)
- However…
- Validity of the members - spelling, existence, etc. - is not checked by the compiler
- Also, intellisense gives you nothing except 2 comments
- That you are working with a "dynamic expression"
- That "This operation will be resolved at runtime."
- Ergo, any errors in your method invocations, property use, etc., will not appear until run-time (ugh…)
- Specifically, you will throw a
RuntimeBinderException
- As such, a wise programmer knows that distracted
dynamic
data use will lead to tears
- Specifically, you will throw a
- The
Microsoft.Csharp.dll
assembly- Automatically referenced by C# projects launched in VS 2010
- All the assembly contains, though, are 2 exceptions
RuntimeBinderException
- the main oneRuntimeBinderInternalCompilerException
- Because of the nature of
dynamic
variables, you really want to employtry/catch
blocks
- The scope of the
dynamic
keyword- Implicitly typed data can only be used for local variables
- Unlike the
var
keyword, however, you can use thedynamic
keyword to define:- Fields
- Properties
- Return types
- Limitations of the
dynamic
keyword- When calling a method dynamic data cannot make use of
- Lamba expressions
- Anonymous methods
- Note: This means that with dynamic data you have to work directly with underlying delegates
- Also, you cannot invoke any extension methods of variables defined with the
dynamic
keyword - This limitation, in turn knocks out the use of any LINQ expressions on
dynamic
variables - (Remember, LINQ expressions are based on externsion methods)
- When calling a method dynamic data cannot make use of
- Practical uses of the
dynamic
keyword- Given limitations of the
dynamic
keyword, a wise developer uses it judiciously - Where it can be useful, though:
- Can make your code much more concise when working with late binding (i.e., reflection)
- Working with COM libraries (this becomes especially valuable with Office automation)
- You never have to use the
dynamic
keyword- In the right circumstances is does save typing
- However, you can always get to your goal sans using
dynamic
- Given limitations of the
- General
- The Dynamic Language Runtime (DLR)
- General
- As of .NET 4.0 the framework includes a runtime environment called the Dynamic Language Runtime (DLR)
- The DLR, of course, is in addtion to the CLR
- Dynamic runtimes not unique to .NET
- Employed by languages such as:
- Smalltalk
- LISP
- Ruby
- Python
- Dynamic runtimes simply allow their languages
- To determine types at runtime
- To ignore compile-time checks
- Employed by languages such as:
- Advantages of dynamic runtimes
- Flexible code - you can refactor code without worrying about data types
- Simple framework for programming against different platforms & languages
- A mechanism for adding or removing members of a type at runtime
- "The role of the DLR is to enable various dynamic languages to run with the .NET runtime and give them a way to interoperate with other .NET code." (Troelsen: 5th Ed., p. 708)
- Again, IronRuby & IronPython are sprecifically written to interact with .NET
- Expression trees
- General
- "An expression tree provides a method of translating executable code into data." (MSDN.com, Charlie Calvert's Community Blog, January 31, 2008)
- Very useful for translating code before executing it
- This may be required when you code is going to be executed by another runtime binder, such as
- SQL Server
- COM servers
- Iron Python
- Iron Ruby
- C# dynamic binder
- When working with the DLR, is that the creation of expression trees is handled for you
- Syntax for an expression tree
- Assume a lambda expression
Func<int, int, int> myFunction = (a,b) => a + b;
- This statement has 3 parts to be handled:
- A declaration:
Func<int, int, int> myFunction
- The assignment operator:
=
- The lamda expression itself:
(a,b) => a + b;
- A declaration:
- The information passed into an expression tree is called its payload
- Assume a lambda expression
- Translating code into data
- "Expression trees are not executable code." (MSDN.com, Charlie Calvert's Community Blog, January 31, 2008)
- Instead, they are a data structure designed to capture the elements of executable code
- You can explicitly create an expression tree for/from a method via the
Expression<TDelegate>
genericExpression<T>
is found in theSystem.Linq.Expressions
namespace- To translate the above lamda statement you would write
Expression<Func<int, int, int>> myExpression = (a,b) => a + b;
- The variable
myExpression
is not executable code, but an expression tree data structure
- Analyzing expression trees
- Can only be done in debug mode
- Available via the magnifying glass icon next to property
- itself is available in & in the windows
- You are then presented with several available viewers
- There is a nice one, in particular, called , that ships with the VS Samples (at least as of VS 2008)
- Writing code to explore an expression tree
Expression<TDelegate>
has 4 propertiesBody
- e.g.,(a + b)
Parameters
- of the lamda expressionNodeType
- Of type
ExpressionType
ExpressionType
is an enumeration of 45 different values- Examples:
- Returning a constant
- Adding 2 values
- Returning parameters
- Running greater-than & less-than on 2 values
- Of type
Type
- The (static) type of the expression
- In above example that type is
Func<int, int, int>
- You then cast some of these properties to
BinaryExpression
&ParameterExpression
types - Syntax/example:
BinaryExpression body = (BinaryExpression)expression.Body;
ParameterExpression left = (ParameterExpression)body.Left;
ParameterExpression right = (ParameterExpression)body.Right;
- Compiling an expression: converting data back into code
- Use the
Compile()
method ofExpression<TDelegate>
- Syntax/example:
int result = myExression.Compile()(3,5);
// Print 8 to the console…
Console.WriteLine(result);
- Use the
IQueryable<T>
& expression trees- When you write a LINQ statement your implicitly-typed local variable (i.e., your
var
variable) is of typeIQueryable
IQueryable
definition:
public interface IQueryable : IEnumerable
{
Type ElementType { get; }
Expression Expression { get; }
IQueryProvider Provider { get; }
}- The
Expression
property in<IQueryable>
is designed to hold an expression tree
- When you write a LINQ statement your implicitly-typed local variable (i.e., your
- Why convert a LINQ to SQL query expression into an expression tree?
- When working with LINQ to SQL your LINQ statement is not executed by the .NET runtime
- Instead, the statement is sent to the database where it is run by the db engine
- Much easier for the db engine to work with an expression tree than it is to work with CIL or, of course, binary
- When working with any other program language it is easier for that external runtime to work with an expression tree than to work with C# code or CIL
IQueryable<T>
&IEnumerable<T>
- LINQ to object relies on
IEnumerable<T>
, not onIQueryable<T>
- Further, the
IEnumerable<T>
interface, of course, only defines a single method,GetEunumerator
IQueryable<T>
&IEnumerable<T>
- The reason for the 'omission' of an
Expression
proprty from LINQ to objects?- LINQ to objects statements are handled by the .NET runtime
- Handled 'internally' as they are, there is no need for LINQ to object statements to be prepped for an external runtime
- LINQ to object relies on
- General
- The
System.Dynamic
namespace- Introduced with .NET 4.0
- Will likely never have to use types within the namespace
- Essentially, contained types are for writing a new dynamic language .NET-aware
- Dynamic runtime lookup of expression trees
- DLR handles the passing of expression trees to a target object
- Manner of doing so varies by type of object being pointed to
- COM
- DLR sends an
IDispatch
type, which is a low-level COM interface IDispatch
is the interface COM uses for working with dynamic types
- DLR sends an
- Non-COM objects
- DLR may pass the expression tree an object implementing
IDynamicObject
interface IDynamicObject
is an interface particularly recognized by Ruby
- DLR may pass the expression tree an object implementing
- Non-COM & does not implement
IDynamicObject
- This means you are dealing with a .NET object
- "In this case,[sic] the expression tree is dispatched to the C# runtime binder for processing. " (Troelsen: 5th Ed., p. 709)
- COM
- General
- Simplifying late-bound calls using dynamic types
- General
- As discussed above, when invoking a method using late binding you essentially take the following steps
- Create an instance of your external assembly
- Get your desired
Type
instance from that assembly - Create an
object
instance of that type - Create a
MethodInfo
instance of that type - Run
Invoke()
on thatMethodInfo
instance - Syntax/example (directly from Troelsen: 5th Ed., p. 710):
static void CreateUsingLateBinding(Assembly asm)
{
try
{
Type miniVan = asm.GetType("CarLibrary.Minivan");
// Create the minivan…
object obj = Activator.CreateInstance(miniVan);
MethodInfo mi = miniVan.GetMethod("TurboBoost");
// Pass in 'null' for no parameters…
mi.Invoke(obj, null);
}
catch(Exception ex)
{ // Handle the Exception… }
}
- Using the
dynamic
keyword, however, makes for more compact code- In particular, you no longer need to:
- Create a
MethodInfo
instance - Run
Invoke()
on that instance
- Create a
- Syntax/example (directly from Troelsen: 5th Ed., pp. 710‑1):
static void InvokeMethodWithDynamicKeyword(Assembly asm)
{
try
{
Type miniVan = asm.GetType("CarLibrary.Minivan");
// Create the minivan…
dynamic obj = Activator.CreateInstance(miniVan);
// Now just call the method…
obj.TurboBoost();
}
catch(Exception ex)
{ // Handle the Exception… }
}
- In particular, you no longer need to:
- As discussed above, when invoking a method using late binding you essentially take the following steps
- Leveraging the
dynamic
keyword to pass arguments- When using
MethodInfo.Invoke()
all paramaters have to be passed in as an array ofobjects
- This means adding a line to the above code a la
object[] args = { // your args… };
- With the
dynamic
keyword you include paramaters in the method invocation code, just as you would normally - Note: remember to implement your
catch
statement as follows:xx
< xx >
- Note: remember to implement your
catch
statement as follows:
catch (Microsoft.Csharp.RuntimeBinder.RuntimeBinderException ex)
{ // Some code… }
- When using
- General
- Simplifying COM interoperability using dynamic data
- General
- COM assemblies contain metadat, just as with .NET
- However, COM metadata is laid out in a completely different format
- Interoperability assemblies (aka, interop assemblies)
- Required for .NET to read COM metadata
- To get a hold of one access dialog box, then from tab select your assembly
- VS then generates .NET assemblies with .NET descriptions of COM metadata
- Only implementation code is what's required to translate COM events to .NET events
- You then program against the interop assemblies
- Runtime Callable Wrappers (RCWs)
- Essentially a "translator" which allows COM and .NET assemblies to speak to each other
- More technically, "basically a dynamically generated proxy" (Troelsen: 5th Ed., p. 714)
- Also handles adjustments to a COM object' reference counter
- Primary Interop Assemblies (PIAs)
- These are the "official" interop assemblies (where availabe) provided by COM library publishers
- These are optimized RCWs
- Sometimes provide extensions
- PIAs listed in tab (i.e., not in tab)
- In fact, if a PIA is available & you point to a COM library VS will automatically redirect matter to the PIA
- Embedding interop metadata
- Pre-.NET 4.0
- To use COM library (PIA or not) you needed to make sure client machine had a copy of the interop
- This complicated & enlarged your installer package
- Installer need to check for the presence of the interop
- If interop was not found installer had to pop the interop into the GAC
- As of .NET 4.0
- You can optionally embed interops right into your compiled app
true
when selecting a COM library automatically sets to- Can verify this by checking
- Notes:
- The automatic embedding occurs regardless of whether or not the COM library is a PIA
- The C# compiler only includes those parts of the interop that are relied upon by your assembly
- All in all, this greatly simplifies instillation issues
- Pre-.NET 4.0
- Common COM interop pain points
- Most eliminated with .NET 4.0
- Optional arguments
- Prior to .NET 4.0 you had to supply
Type.Missing
for each & every such argument - Now you can simply ignore
- Even easier still now that you can work with named arguments
- Prior to .NET 4.0 you had to supply
- COM'S
Variant
type- Used to define both parameters and/or return types with many COM methods
- Akin to
dynamic
type - designed to allow COM programs to assign types on the fly (i.e., at runtime) - Before introduction of
dynamic
keyword you had to do a lot of casting to working with thevariant
type - Setting
true
property to- Reminder: this is the default in VS
- All COM
variant
types set todynamic
- Eliminates the need for casting operations in your code
- Also hides "some COM-complexities, such as working with COM indexers" (Troelsen: 5th Ed., p. 718)
- Note: if you want to work with COM objects using strongly-typed data you have to dive into the
System.Runtime.InteropServices
namespace
- General
- COM interop using C# 4.0 language features
- General
- COM interop without C# 4.0 language features
- The C#
- Configuring .NET Assemblies
- .NET Base Class Libraries
- Multithreaded & Parallel Programming
- The Process/AppDomain/Context/Thread relationship
- General
- The problem of concurrency
- Thread synchronization
- Delegates
- Reviewing the .NET delegate
- Asynchronous nature of delegates
- General
- The
BeginInvoke()
&EndInvoke()
methods - The
System.IAsyncResult
interface
- Invoking a method asynchronously
- General
- Synchronizing the calling thread
- The
AsyncCallback
delegate - The
AsyncResult
class - Passing & receiving custom state data
- The
System.Threading
namespace - The
System.Threading.Thread
class- General
- Obtaining statistics about the current thread
- The
Name
property - The
Priority
property
- Programming with threads
- Programmatically creating secondary threads
- General
- The
ThreadStart
delegate - The
ParameterizedThreadStart
delegate - The
AutoResetEvent
class - Foreground threads & background threads
- The issue of concurrency
- General
- Synchronization using the C#
lock
keyword - Synchronization using the
System.Threading.Monitor
type - Synchronization using the
System.Threading.Interlocked
type - Synchronization using the
[Synchronization]
attribute
- Programming with timer callbacks
- The CLR
ThreadPool
- Programmatically creating secondary threads
- Parallel programming
- Parallel programming under the .NET platform
- General
- The Task Parallel Library (TPL) API
- The
Parallel
class - Data parallelism
- The
Task
class - Handling cancelation request
- Task parallelism
- Parallel LINQ queries (PLINQ)
- General
- Opting into a PLINQ query
- Canceling a PLINQ query
- Parallel programming under the .NET platform
- The Process/AppDomain/Context/Thread relationship
- File I/O & Object Serialization
- File I/O
- The
System.IO
namespace- Assemblies
- Most types in the namespace exist in
mscorlib.dll
- Some exist in
System.dll
- Note: all VS 2010 projects automatically include references to both assemblies
- Most types in the namespace exist in
- Core non-abstract members of the namespace
BinaryReader, BinaryWriter
- used to store & retrieve primitive data types as binary valuesBufferedStream
- Gives you temporary storage for a stream of bytes
- Can later commit that info to storage
Directory(Info)
&File(Info)
Directory
&File
expose static membersDirectoryInfo
&FileInfo
have similar capabilities but work via particular object references- Note: when creating instances of the
DirectoryInfo
&FileInfo
types you will be 'binding' those instances to particular files & directories
DriveInfo
FileStream
&MemoryStream
- Represent data as a stream of bytes
- Provide random access, which has to do with seeking capabilities
FileSystemWatcher
Path
- Operates on
System.String
types containing file or directory path information - Platform-neutral
- Operates on
StreamWriter, StreamReader
- Use to store & retrieve textual info to/from a file
- Do not support random file access
StringWriter, StringReader
- Similar to
StreamWriter, StreamReader
- However, work on string buffers rather than on files
- Similar to
- Abstract classes
Stream
TextReader
TextWriter
- Various enumerations
- Assemblies
- The
Directory(Info)
&File(Info)
types- General
- Inheritance
File
&Directory
types inherit directly fromSystem.Object
FileSystemInfo
- Abstract class
- Inherits directly from
System.Object
- Direct parent of
FileInfo
&DirectoryInfo
- Features & usage
DirectoryInfo
&FileInfo
have to be allocated (i.e., 'new-ed')Directory
&File
tend to give you 'only' string valuesDirectoryInfo
&FileInfo
- Return strongly-typed objects
- Generally are better options than
Directory
&File
in giving you full details of the item in question
- Inheritance
- The abstract
FileSystemInfo
base class- Properties
Attributes
- returns members of theFileAttributes
enumerationCreationTime
Exists
Extension
(not applicable for a directory, of course)FullName
LastAccessTime
LastWriteTime
Name
- Methods
Delete()
Refresh()
- invoke to update properties
- Properties
- The
DirectoryInfo
type- General
- Members (beyond those inherited from
FileSystemInfo
)- Methods
Create(), CreateSubdirectory()
Delete()
GetDirectories()
- returns an array ofDirectoryInfo
objectsGetFiles()
- returns an array ofFileInfo
objectsMoveTo()
- Properties
Parent
- gives you parent directoryRoot
- gives you root portion of directory's path
- Methods
- Allocating
DirectoryInfo
objects- Specify directory path as a construction parameter
- Note: use
"."
notation to specify current directory (i.e., of the app)
- Non-existent & new directories
- Nothing prevents you from binding a
DirectoryInfo
object to a non-existent directory - In fact, that is precisely what you do when creating a directory
- Syntax/example:
DirectoryInfo dirNew = new Directory(@"C:\Foo");
dirNew.Create(); - However, CLR will throw a
System.IO.DirectoryNotFoundException
if it tries to interact with a non-existent directory
- Nothing prevents you from binding a
- Members (beyond those inherited from
- Enumerating files with the
DirectoryInfo
type- Again, the
GetFiles()
method returns an array ofFileInfo
objects GetFiles()
has been overloaded
- Again, the
- Creating subdirectories with the
DirectoryInfo
type- Can create both folders & nested sub-folders
- Syntax/example:
static void ModifyAppDirectory()
{
DirectoryInfo dir = new DirectoryInfo(@"C:\");
// Create a nested sub-folder…
dir.CreateSubdirectory(@"MyFolder2\Data");
} - Note: you can capture the return value of
CreateSubdirectory
(i.e., aDirectoryInfo
object), though, as example just showed, this is optional
- General
- The
Directory
type- Methods here are pretty much the same as those of
DirectoryInfo
type - However, returned values generally are
strings
rather thanDirectoryInfo/FileInfo
types - Syntax/example:
static void WorkingWithDirectoryType()
{
// List all drives on the computer…
string[] drives = Directory.GetLogicalDrives();
foreach (string s in drives)
Console.WriteLine("--> {0}", s);
// Delete previously-created folders…
try
{
Directory.Delete(@"C:\MyFolder");
// 2nd param controls deleting subdirectories…
Directory.Delete(@"C:\MyFolder2", true);
}
catch (IOException e) { Console.WriteLine(e.Message); }
}
- Methods here are pretty much the same as those of
- The
DriveInfo
class type- Whereas
Directory.GetLogicalDrives()
gives you an array ofstrings
,DriveInfo.GetDrives()
returns an array ofDriveInfo
objects - Names, volume labels, etc. are available from a
DriveInfo
type - Note: after declaring, say,
DriveInfo d
to be some drive, used.IsReady
to filter for whether drive is mounted
- Whereas
- The
FileInfo
class- General
- Again,
FileInfo
inherits fromFileSystemInfo
- Methods declared by
FileInfo
AppendText()
- creates aStreamWriter
object to do soCopyTo()
Create()
- Creates a new file
- Returns a
FileStream
object that you use to work with that file
CreateText()
- Creates aStreamWriter
that will write the text for youDelete()
- deletes to file to which yourFileInfo
instance is boundMoveTo()
- Note: can simultaneously rename your file with this methodOpen()
- can specify various read/write privileges in the processOpenRead()
- creates a read-onlyFileStream
objectOpenText()
- creates aStreamReader
to read from a text fileOpenWrite()
- creates a write-onlyFileStream
object
- Properties of
FileInfo
Directory
- gives you parent directoryDirectoryName
- full name of parent directoryLength
- file sizeName
- Again,
- The
FileInfo.Create()
method- This is how you obtain a file handle
- Syntax/example:
static void Main(string[] args)
{
// Make a new file on the C drive…
FileInfo f = new FileInfo(@"C:\MyData.dat");
FileStream fs = f.Create();
// Use the FileStream object…
// Close the file stream…
fs.Close();
} - Notes:
- Can work with
FileStream
object on both synchronous & asynchronous basis - For terser code employ a
using
block
- Can work with
- The
FileInfo.Open()
method- Gives you far more precision than
FileInfo.Create()
- As with
FileInfo.Create()
you are returned aFileStream
object - However,
FileInfo.Open()
takes several very useful parameters FileMode
enumerationCreateNew
&Create
- With either enumeration a new file is created
- If the 'new' file already exists
CreateNew
will throw anIOException
Create
, on the other hand, will overwrite the existing file
Open
- can throw aFileNotFoundException
OpenOrCreate
Truncate
- opens an existing file & blows out all content (file size will be 0 bytes)Append
- Can only use this parameter with a write-only stream
- Creates the file if it does not already exist
- If the file does exist moves to the end of the file after opening it & begins write operations
FileAccess
enumerationRead
Write
ReadWrite
FileShare
enumeration- This governs how the file is shared among other file handlers
- Values
Delete
Inheritable
None
Read
Write
ReadWrite
- Gives you far more precision than
- The
FileInfo.OpenRead()
&FileInfo.OpenWrite()
methods- Returns a read-only or write-only
FileStream
object - A little simpler than
FileInfo.Open()
method where parameters must be passed in
- Returns a read-only or write-only
- The
FileInfo.OpenText()
method- Returns a
StreamReader
object, rather than aFileStream
object StreamReader
allows you to read character, rather than byte, data
- Returns a
- The
FileInfo.CreateText()
&FileInfo.AppendText()
methods- Both methods return a
StreamReader
object - Can (obviously) then write character data to the file of interest
- Both methods return a
- General
- The
File
type- General
- Has static methods pretty much identical to those of
FileInfo
instances - Advantage: this means that you don't have to create an instance (i.e., a
FileInfo
type) to work with a file - Implements
IDisposable
- Has static methods pretty much identical to those of
- Additional
File
-centric methodsReadAllBytes()
- returns an array of bytesReadAllLines()
- returns character data as an array ofstrings
ReadAllText()
- returns a singleSystem.String
WriteAllBytes()
- writes out an array of bytesWriteAllLines()
- writes out an array ofstrings
WritesAllText()
- writes from a specifiedstring
- Notes:
- All these methods open the specified file, ' do their thing', & then close the file automatically
- Again, working with
File
is generally simpler & quicker than working withFileInfo
objects
- Syntax/example:
static void Main(string[] args)
{
string[] mytasks = { "Fix bathroom sink", "Call Denise",
"Call Mom", "Play backgammon" };
// Write out all data to a file on C drive…
File.WriteAllLines(@"C:\tasks.txt", mytasks);
// Read it all back and print out…
foreach (string task in File.ReadAllLines(@"C:\tasks.txt"))
{
Console.WriteLine("TODO: {0}", task);
}
Console.ReadLine();
}
- General
- General
- Streams, readers, & writers
- The abstract
Stream
class- General
- A stream "represents a chunk of data flowing between a source and a destination" (Troelsen: 5th Ed., p. 792)
- Streams nothing more than a sequence of bytes
- Streams also provide a layer of abstraction
- You work with data without caring whether that stream comes from a disk file, web service, etc.
- Also means you can be indifferent to what device will store/consume the stream
- Stream is not even limited to file IO
System.IO.Stream
provides both synchronous & asynchronous interaction methods- Seeking
- Supported by some
Stream
-derived types - Relates to finding (&, possibly, moving) current position in a stream
- Supported by some
- Properties
CanRead, CanWrite
, &CanSeek
Length
- measured in bytesPosition
- Methods
Close()
- An alias for
Dispose()
- Closes stream & releases any related resources
- An alias for
Flush()
- Updates data source/repository
- Then clears the buffer
- If the stream does not support a buffer does nothing
Read()¦ReadByte()
&Write()¦WriteByte()
- Reads/writes either a single byte or a sequence of bytes from/to stream
- Then advances the position in the stream by number of bytes read/written
Seek()
- sets the position in the streamSetLength()
FileStreams
- An implementation of
Stream
- Fairly primitive - can only read & write single bytes or array of bytes
- In practice…
- Generally do not interact directly with
FileStream
types FileStream
types require you to work with (ugh) raw bytes- Instead you work with any number of stream wrappers
- Generally do not interact directly with
- Syntax/example:
static void Main(string[] args)
{
// Obtain a FileStream object….
using (FileStream fStream =
File.Open(@"C:\myMessage.dat", FileMode.Create))
{
// Encode a string as an array of bytes…
string msg = "Hello!";
byte[] msgAsByteArray = Encoding.Default.GetBytes(msg);
// Write byte[] to a file…
fStream.Write(msgAsByteArray, 0, msgAsByteArray.Length);
// Reset internal position of the stream…
fStream.Position = 0;
// Read the types from file and display to console…
Console.Write("My message as an array of bytes: ");
byte[] bytesFromFile = new byte[msgAsByteArray.Length];
for (int i = 0; i < msgAsByteArray.Length; i++)
{
bytesFromFile[i] = (byte)fStream.ReadByte();
Console.Write(bytesFromFile[i]);
}
// Display decoded msg…
Console.WriteLine("\nDecoded Message: ");
Console.WriteLine(Encoding.Default.GetString(bytesFromFile));
}
Console.ReadLine();
} - Notes:
- Again, limitation of
FileStreams
is necessity of working on raw bytes - Other
Stream
-derived types also force this on you - Examples:
- You use
MemoryStream
to push a sequence of bytes into memory - You use
NetworkStream
to push a sequence of bytes through a network connection
- You use
- Again, limitation of
- An implementation of
- General
StreamWriters
&StreamReaders
- General
- Use these when you want to work with character-based data
- Character set
- Unicode by default
- Can change that, though, with a
System.Text.Encode
object type
- Inheritance
StreamWriter
(&StringWriter
) inherit from abstractTextWriter
StreamReader
(&StringReader
) inherit from abstractTextReader
- Members of
TextWriter
Close()
- Equivalent to
Dispose()
- Flushes buffer
- Frees any related resources
- Equivalent to
Flush()
- does not close writerWrite()
- Overloaded
- Does not embed any newline constants
WriteLine()
- Overloaded
- Embeds a newline constant
NewLine
- Gets/sets the newline constant
- Note: default for Windows is
\r\n
- i.e., a carriage return & then a line feed
- Members of
StreamWriter
- Implementations of
Write(), Close()
, &Flush()
AutoFlush
property- When set to
true
flushes all data with every write operation - Using
AutoFlush
will hurt your performance a bit - Note: remember that invoking
Close()
automatically invokesFlush()
- When set to
- Implementations of
- Writing to a text file
- Syntax/example:
static void Main(string[] args)
{
// Get a StreamWriter and write string data…
using (StreamWriter writer = File.CreateText("reminders.txt"))
{
writer.WriteLine("Don't forget Mother's Day this year…");
writer.WriteLine("Don't forget Father's Day this year…");
writer.WriteLine("Don't forget these numbers:");
for (int i = 0; i < 10; i++)
writer.Write(i + " ");
// Insert a new line…
writer.Write(writer.NewLine);
}
} - Note: use of
NewLine
property
- Syntax/example:
- Reading from a text file
- Abstract
TextReader
membersPeek()
- Reads the next character in the stream without changing the position of the reader
- If at the end of the stream you will get a value of
-1
Read()
ReadBlock()
- Pass in beginning index & max number characters to read
- Writes the data that's been read to a buffer
ReadLine()
- Returns the line read as a
string
- If at the end of the file you will receive a
null
string
- Returns the line read as a
ReadToEnd()
- Begins reading at current position
- Returns a single string
StreamReader
derives fromTextReader
- Syntax/example:
static void Main(string[] args)
{
// Now read the data from a text file…
using (StreamReader sr = File.OpenText("reminders.txt"))
{
string input = null;
while ((input = sr.ReadLine()) != null)
{ Console.WriteLine(input); }
}
Console.ReadLine();
}
- Abstract
- Directly creating
StreamWriter/StreamReader
types- As with much else in the
System.IO
namespace, you can get your hands on aStreamWriter
orStreamReader
object in more ways than one - Specifically, rather than catch the result of an
File.OpenText()
orFile.CreateText()
method you can create your types directly - Syntax/example:
// To get a StreamWriter…
using(StreamWriter writer = new StreamWriter("foo.txt"))
{ … }
// To get a reader…
using(StreamReader sr = new StreamReader("foo.txt"))
{ … }
- As with much else in the
- General
StringWriters
&StringReaders
- Use these 2 types when you are simply working with in-memory character data (i.e., you do not need or want to work with a file)
- Note: you can also obtain a
System.Text.StringBuilder
object from aStringWriter
- Syntax/example:
static void Main(string[] args)
{
// Create a StringWriter & emit character data to memory…
using (StringWriter strWriter = new StringWriter())
{
strWriter.WriteLine("I'm Mr. Manager!");
// Push to the console…
Console.WriteLine("Contents of StringWriter:\n{0}", strWriter);
// Get the internal StringBuilder…
StringBuilder sb = strWriter.GetStringBuilder();
sb.Insert(0, "Hey!! ");
Console.WriteLine("-> {0}", sb.ToString());
sb.Remove(0, "Hey!! ".Length);
Console.WriteLine("-> {0}", sb.ToString());
}
Console.ReadLine();
} StringReader
functionality is exactly the same as that ofStreamReader
BinaryWriters
&BinaryReaders
- Both inherit directly from
System.Object
- Select
BinaryWriter
membersBaseStream
- read-onlyClose()
Flush()
Seek()
- sets the position in the current streamWrite()
- highly overloaded
- Select
BinaryReader
membersBaseStream
- read-onlyClose()
PeekChar()
- returns next character in the stream without advancing the positionRead()
- stores bytes or characters in the incoming arrayReadXXX()
- There are numerous versions, which grab the next appropriate type from the stream
- Examples:
ReadBoolean(), ReadByte()
, etc.
- Syntax/example:
static void Main(string[] args)
{
// Open a binary writer for a file…
FileInfo f = new FileInfo("BinFile.dat");
using (BinaryWriter bw = new BinaryWriter(f.OpenWrite()))
{
// Save some data into the file…
double aDouble = 1234.5678;
int anInt = 9876;
string aString = "I'm Mr. Manager!";
// Write the data…
bw.Write(aDouble);
bw.Write(anInt);
bw.Write(aString);
}
// Read the binary data from the stream…
using (BinaryReader br = new BinaryReader(f.OpenRead()))
{
Console.WriteLine(br.ReadDouble());
Console.WriteLine(br.ReadInt32());
Console.WriteLine(br.ReadString());
}
Console.ReadLine();
} - Notes:
FileStream
object is passed into constructor ofBinaryWriter
viaFileInfo.OpenWrite()
- "Using this technique makes it easy to layer in a stream before writing out the data." (Troelsen: 5th Ed., p. 801)
BinaryWriter
constructor takes anyStream
-derived type as its parameter, e.g.…FileStream
MemoryStream
BufferedStream
- Both inherit directly from
- The abstract
- Watching files programmatically
- Use the
FileSystemWatcher
class- Uses
System.IO.NotifyFilters
enumerationAttributes
CreationTime
DirectoryName
FileName
LastAccess
LastWrite
Security
Size
- Must pass in
Path
property (of the directory to monitor)Filter
property - to specify which files to watch
- Uses
FileSystemEventHandler
delegate- This is what you want to create to fire when a file changes
- Use the
- The
- Serialization
- Object serialization
- General
- Object graphs
- Configuring objects for serialization
- General
- Defining
Serializable
types public
fields,private
fields, &public
properties
- Choosing a serialization formatter
- General
- The
IFormatter
&IRemotingFormatter
interfaces - Type fidelity among the formatters
- Serialization options
- Serializing objects using the
BinaryFormatter
- General
- Deserializing objects using the
BinaryFormatter
- Serializing objects using the
SoapFormatter
- Serializing objects using the
XmlFormatter
- General
- Controlling the generalted XML data
- Serializing objects using the
- Serializing collections of objects
- Customizing the Soap/Binary serialization process
- General
- A deeper look at object serialization
- Customizing serialization using
ISerializable
- Customizing serialization using attributes
- Object serialization
- File I/O
- ADO.NET
- The Connected Layer
- Connecting to databases
- High-level definition of ADO.NET
- General
- Classic ADO "was primarily designed for tightly coupled client/server systems…" (Troelsen: 5th Ed., p. 825)
- ADO.NET has heavy emphasis on disconnected approach, via
DataSets
- local copy of related data tables - Modified data is returned to database via a data adapter
- Bulk of ADO.NET in
System.Data.dll
- Most namespaces in this assembly correspond to a specific ADO.NET data provider (e.g., for Oracle, SQLServer, etc.)
- The three faces of ADO.NET
- Connected
- Disconnected
- Entity Framework
- General
- ADO.NET data providers
- General
- ADO.NET supports multiple data providers
- Each data provider constructed to interact with a specific DBMS
- Advantages of this approach
- These specific data providers enable you to access unique features of DBMS
- No need for a further, intermediate layer
- Types common to each data provider (& each type's base class)
Connection (DbConnection)
- Relevant interface(s):
IDbConnection
- Provides mechanism to connect & disconnect from data store
- Provides access to transaction object
- Also provides access to parameter collection
- Relevant interface(s):
Command (DbCommand)
- Relevant interface(s):
IDbCommand
- "Represents a SQL query or a stored procedure." (Troelsen: 5th Ed., p. 827)
- Also use this to access provider's data reader object
- Relevant interface(s):
DataReader (DbDataReader)
- Relevant interface(s):
IDataReader, IDataRecord
- Use for forward-only, read-only access to data via a server-side cursor
- Relevant interface(s):
DataAdapter (DbDataAdapter)
- Relevant interface(s):
IDataAdapter, IDbDataAdapter
- User to transfer
DataSets
- Data adapters contain a connection and 4 internal command objects which enable you to select, insert, update, & delete from data store
- Relevant interface(s):
Parameter (DbParameter)
- Relevant interface(s):
IDataParameter, IDbDataParameter
- Used to represent a named parameter in a parameterized query
- Relevant interface(s):
Transaction (DbTransaction)
- Relevant interface(s):
IDbTransaction
- Represents a transaction
- Relevant interface(s):
- Notes on these types
- All base classes are defined within
System.Data.Common
namespace - All interfaces are defined within
System.Data
namespace - The names of common types will all be 'customized' slightly - e.g.,
SqlConnection
vs.OracleConnection
, etc. - In fact, there are no types named
Connection, Command
, etc.
- All base classes are defined within
- Microsoft-supplied ADO.NET data providers
- Major ones shipped with .NET (Data Provider; Namespace; Assembly)
- OLE DB;
System.Data.OleDb; System.Data.dll
- Use this when you have to use COM-based OLE DB protocol
- Simply adjust
Provider
segment of connection string to hook to any OLE db - Note: when using this approach you interact with various COM objects 'behind the scenes', which can degrade performance
- With the exception of MSSS version 6.5 or earlier you should always prefer, & can almost always find, a custom ADO.NET data provider for download
- MS SQL Server;
System.Data.SqlClient; System.Data.dll
- Works with MSSS versions 7.0 & later
- Bypasses the OLE DB layer - for much better performance
- MS SQL Server Mobile;
System.Data.SqlServerCe; System.Data.SqlServerCe.dll
- ODBC;
System.Data.Odbc; System.Data.dll
- you only want to use this for a DBMS for which there is no custom .NET data provider
- OLE DB;
- Microsoft Access
- There is no data provider for the Jet engine
- If you must tap Access user the OLE DB or ODBC data provider
- Major ones shipped with .NET (Data Provider; Namespace; Assembly)
- Using
System.Data.OracleClient.dll
- As of .NET 4.0 the Oracle data provider shipped by MSFT has been marked as obsolete
- Use Oracle Data Access Components (ODAC) for Windows, available directly from Oracle
- Third-party data providers
- Generally you get non-MSFT data providers from appropriate vendor
- Good third-party source
- General
- Additional ADO.NET namespaces
Microsoft.SqlServer.Server
System.Data
- Contains core ADO.NET types used by all data providers
- Contains many of the types used by the disconnected layer
System.Data.Common
- Contains types used by all ADO.NET data providers
- Particularly includes common abstract base classes
System.Data.Sql
- "contains types that allow you to discover Microsoft SQL Server instances installed on the current local network." (Troelsen: 5th Ed., p. 831)System.Data.SqlTypes
- For native data types used by MSSS
- You always have the option to use CLR data types
- However, the
SqlTypes
are optimized to work with MSSS - Example: you are generally better off using
SqlTypes.SqlInt32
than usingint
- Types of the
System.Data
namespace- General
- You must specify this namespace in any ADO.NET data access application, regardless of RDMS provider being used
- Namespace contains types shared among all ADO.NET data providers
- Many database-centric exceptions
- Various database 'primitives' - e.g., tables, rows, columns, etc.
- Many interfaces implemented by data provider objects
- Core members (especially used with disconnected layer of ADO.NET):
Constraint
(for aDataColumn
object)DataColumn
DataRelation
- specifies parent/child relation between twoDataTable
objectsDataRow
DataSet
"Represents an in-memory cache of data consisting of any number of interrelatedDataTable
objects." (Troelsen: 5th Ed., p. 832)DataTable
(in-memory tabular data)DataTableReader
"Allows you to treat aDataTable
as a fire-hose cursor (forward only, read-only data access)." (Troelsen: 5th Ed., p. 832)DataView
IDataAdapter
- defines the core behavior of a data adapter objectIDataParameter
IDataReader
IDbCommand
IDbDataAdapter
- extends functionality ofIDataAdapter
IDbTransaction
- Interface definitions (see Troelsen: 5th Ed., pp. 833-7)
IDbConnection
interfaceIDbTransaction
interfaceIDbCommand
interfaceIDbDataParameter
&IDataParameter
interfacesIDbDataAdapter
&IDataAdapter
interfacesIDataReader
&IDataRecord
interfaces
- General
- Abstracting data providers using interfaces
- General
- Benefit of deriving data providers from interfaces is that your app can be RDMS-neutral
- Example:
public static void OpenConnection(IDbConnection cn)
{
// Open the incoming connection for the caller…
cn.Open()
} - Note: If desired you can accomplish the same thing by eschewing interfaces and using abstract base classes, such as
DbConnection
, as parameters or return values - In practice you use the object factory design pattern (see Troelsen: 5th Ed., pp. 837-8, for example)
- Use reflection services
- Use a
switch
statement to, say, create a connection object based on your RDMS
- This all obviates having to write new code or even to recompile your project should you decide to move from, say, Oracle to MSSS
- Increasing flexibility using application configuration files
- Can use custom name/value pairs in the
<appSettings>
element of the*.config
file - Can obtain this data at runtime using
System.Configuration
namespace - Syntax/example:
<configuration>
<appSetting>
<!-- This key value maps to a custom enum value -->
<add key="provider" value="SqlServer"/>
</appSetting>
</configuration> - If you move your app to a new DBMS then you only need update
*.config
file - Reminder: Must reference
System.Configuration
namespace in your project to do this - To make this all really useful
- Would need to move the code for data provider factory library into a
*.dll
file, so you could use this across multiple applications - Would also need to create factory libraries for command objects, data readers, etc., etc.
- Would need to move the code for data provider factory library into a
- Fortunately, MSFT has provided exactly this kind of object factory since .NET 2.0
- Can use custom name/value pairs in the
- General
- The ADO.NET data provider factory model
- General
- Allows you to build one code base for multiple platforms
- You place providers & connections strings inside
<connectionStrings>
sub-element of app config file - Every MSFT-supplied data provider contains a class type that derives from
System.Data.Common.DbProviderFactory
- Relevant elements:
DbProviderFactory
{
\\…
public virtual DbCommand CreateCommand();
public virtual DbCommandBuilder CreateCommandBuilder();
public virtual DbConnection CreateConnection();
public virtual DbConnectionStringBuilder
CreateConnectionStringBuilder();
public virtual DbDataAdapter CreateDataAdapter();
public virtual DbDataSourceEnumerator
CreateDataSourceEnumerator();
public virtual DbParameter CreateParameter();
} - To access the
DbProviderFactory
-derived type for a data provider:- Call static
GetFactory()
method onDbProviderFactorys
(plural) class, which is found inSystem.Data.Common
namespace - The
GetFactory()
method takes a string argument identical to the .NET namespace for the appropriate provider - Note: rather than passing in that namespace as a literal it would be better to access it from a client-side
*.config
file
- Call static
- A complete data provider factory example (see Troelsen: 5th Ed., pp. 848-51)
- A potential drawback with the Provide Factory model
- Restricted to types & methods in abstract base classes
- If you need to access a vendor-specific method must start wrapping your connection objects inside
if
statements - Example:
if (cn is SqlConnection)…
- This can obviously get cumbersome to write & maintain
- The
<connectionStrings>
element- An alternative to putting connection string data inside the
<appSetting>
element of your*.config
file - Inside the
<ConnectionStrings>
element you include 1 or more<add>
elements - Inside
<add>
elements employname
&connectionString
attributes - Syntax/example:
<configuration>
<appSetting>
<add key="provider" value="System.Data.SqlClient" />
<appSetting>
<connectionStrings>
<add name ="SqlProvider" connectionString = "stuff…" />
<add name ="OleProvider" connectionString = "stuff…" />
<!-- other connection strings, as needed -->
</connectionStrings>
</configuration> - Can then retrieve your connection string via name indexer
- Example:
string cnStr = ConfigMgr.ConnStrgs["SqlProvider"].ConnStr;
- An alternative to putting connection string data inside the
- General
- High-level definition of ADO.NET
- Retrieving & manipulating data
- The Connected layer of ADO.NET
- Objects & steps required to read data
Connection
object- Allocate & configure
- Open
Command
object- Allocate & configure
- Can pass in
connection
object as a parameter tocommand
object constructor - Alternatively, can use
Connection
property aftercommand
object has been created
- Invoke
ExecuteReader()
on configuredcommand
class/object (this returns aDataReader
object) - On DataReader object (e.g.,
SqlDataReader
):- Invoke
Read()
method - Note: this method gives you each record, one by one
- Typically, you use the data reader with a line like:
while (myDataReader.Read())
{ //code… }
- Invoke
Connection
objects- Set up connection object first when working with a data source
- Connection string
- Contains semi-colon delimited name/value pairs
- How a connection string is constructed will depend of DBMS vendor (see SDK documentation)
- Members of the connection object
Open()
&Close()
BeginTransaction()
ChangeDatabase()
(connection must be open)ConnectionTimeout
Database
DataSource
- gives location of databaseGetSchema()
- returns aDataTable
object containing schema infoState
- gives youConnectionState
enumerationBroken
(reserved for future use)Closed
Connecting
(reserved for future use)Executing
(reserved for future use)Fetching
(reserved for future use)Open
- Notes re properties:
- Almost all are read-only
- Values generally set via specifics of connection string
ConnectionStringBuilder
objects- Use these to create name/value pairs via strongly-typed properties
- Syntax/example:
// inside some method…
SqlConnectionStringBuilder cnStrBuilder =
new SqlConnectionStringBuilder();
cnStrBuilder.InitialCatalog = "MyDbTable"
cnStrBuilder.ConnectTimeout = 30;
// etc.…
using(SqlConnection cn = new SqlConnection())
{
cn.ConnectionString = cnStrBuilder.ConnectionString;
cn.Open();
// Other code…
} - Alternately…
- When new-ing the
ConnectionStringBuilder
you can pass an existing connection string as an argument to the constructor - Especially useful if you have such data in a
*.config
file - You can then change individual properties as needed
- When new-ing the
- Command objects
- All command objects derive from
DbCommand
- Command objects are OO representations of either:
- SQL query
- Table name
- Stored procedure
- Specify type of command by using enums of
CommandType
propertyStoredProcedure
TableDirect
Text
(default value)
- When creating command object must also specify connection, which is done via either
- Construction parameter
Connection
property
- Creating a command object does not submit the SQL query!!
- Members of
DbCommand
type:CommandTimeout
- Sets time to wait before bailing on executing a command & throwing an exception
- Default = 30"
Connection
Parameters
Cancel()
ExecuteReader()
- Executes an SQL query
- Returns vendor-specific
DbDataReader
object
ExecuteNonQuery()
(e.g., insert, update, etc…)ExecuteScalar()
- Essentially a lightweight
ExecuteReader()
method - Used for singleton queries (e.g., getting a record count)
- Essentially a lightweight
Prepare()
- Generates a prepared/compiled version of a query
- These run faster than ad hoc queries
- Especially useful if a given query is to be run multiple times
- All command objects derive from
- Objects & steps required to read data
- Data readers
- General
- Data readers vs. a
DataSet
- A
DataSet
holds all records in memory - which is a big consideration with a lot of records - On the other hand, a data reader requires an open connection to the db
- A
- You create a data reader by invoking
ExecuteReader()
method (typically on a command object) - Getting a specific column in a data reader
- Use the data reader's indexer method
- Can pass in either a string or an int (zero-based)
- To loop over records
- Syntax:
while (myDataReader.Read())
{// code… } Read()
method returnsfalse
when end of records is reached
- Syntax:
- Data readers vs. a
- Obtaining multiple result sets using a data reader
- Use a semi-colon delimiter to put together multiple SQL commands into one command string
- Syntax/example:
string strSQL = "Select * from Table1; Select * from Table2"
- After reading over 1st result use
NextResult()
method
- General
- Building a reusable data access library
- General
- In typical production environment you:
- Place ADO.NET logic in a
*.dll
file - Implement
IDisposable
& create a finalizer
- Place ADO.NET logic in a
- Note: When firing
ExecuteNonQuery()
you are returned anint
representing number of rows affected - Suggested approach
- Create 2 namespaces
SomeNameConnectedLayer
SomeNameDisconnectedLayer
- Use
DAL
as suffix for class name (for Data Access Layer) - Troelsen (5th Ed., p. 862) effectively suggests creating a DAL-named class for each table in Db (e.g., InventoryDAL)
- Create 2 namespaces
- In typical production environment you:
- Mechanics
- Connection logic
- Declare a class-wide, private variable of, say,
SqlConnection
type - Create a
OpenConnection()
method, taking connection string as an argument- Use parameter to set
ConnectionString
property of class connection object - Run
Open()
method on connection object
- Use parameter to set
- Create a
CloseConnection()
method, which simply runsClose()
on connection object - Add appropriate exception handlers
- Declare a class-wide, private variable of, say,
- Insertion logic
- When adding column-by-column values to be passed into SQL Insert statement can add values as strings, ints, etc.
- Better approach:
- Create, say, a
NewCar
object with the input values - Pass that car to the insertion logic, then read the properties
- Create, say, a
- Best approach:
- Don't create SQL strings in your code! (you open yourself up to SQL injection attacks)
- Instead, use parameterized queries
- Deletion logic (absolutely employ
try/catch
logic here!) - Update logic
- Selection logic
- Already have shown how to use data reader objects
- Issue: How do you return a bunch of records to your app's calling tier?
- Can read each record into an array
- Can read each record into a strongly-typed collection (e.g.,
List<T>
object)
- Best approach (in most cases):
- Create a method which returns a
DataTable
object. - Note:
DataTable
object is part of ADO.NET's disconnected layer - Create a
System.Data.DataTable
object inside the method - Create data reader object, & run its
ExecuteReader()
method - Run
Load()
method of theDataTable
object, passing in the data reader as a parameter - Close the data reader object
- Create a method which returns a
- Parameterized command objects
- For many obvious reasons it is better to treat SQL parameters as objects rather than as raw text/string
- Command objects hold an initially-empty collection of parameter objects
- With MSSS you prefix a text parameter with
@
symbol
- Connection logic
- Specifying parameters using the
DbParameter
type- General
- Properties (read/write)
DbType
- native type of parameter, as a CLR typeDirection
- input-only, return, etc.IsNullable
ParameterName
Size
Value
- Params work best with stored procedures
- Properties (read/write)
- Executing a stored procedure
- Inside the procedure that will invoke the stored procedure wrap the following inside a
using
statement forCommand
object:- Set
CommandType
property toStoredProcedure
- Declare whatever
Parameter
objects need to be declared, setting appropriate properties - Add each
Parameter
object toParameters
collection - Fire
ExecuteNonQuery()
method ofCommand
object - If looking for a return/out parameter simply collect it (via indexer) from
Parameters
collection
- Set
- Outside
using
scopereturn
whatever value the procedure needs to return
- Inside the procedure that will invoke the stored procedure wrap the following inside a
- General
- General
- Creating a console UI-based front end
- General
- Add reference to DAL.dll
- Add
using
statement for DAL.dll - Place connection string in
App.config
file
- In
Main()
method- Create appropriate variable for connection string
- Create new variable of type DAL.dll
- Passing in connection string invoke
OpenConnection()
method - Add a
switch
statement that invokes appropriate method based on user-supplied letter/code - For each such method pass DAL.dll variable as a parameter
- How to implement methods
- Add, Update, etc. methods should be static
- Make sure to employ
try/catch
blocks in appropriate methods (e.g.,Delete
)
- See Troelsen: 5th Ed., pp. 871-7, for full code example
- General
- Database transactions
- General
- See section on Transactions (in ANSI SQL 2003)
- .NET support for database transactions:
System.EnterpriseServices
namespace- In
System.EnterpriseServices.dll
- Allows integration with COM+ runtime layer (including distributed transactions support)
- In
System.Transactions
namespace- In
System.Transactions.dll
- Contains a number of classes
- Use these classes to write transactional apps & resource managers against services such as MSMQ, ADO.NET, & COM+
- In
- Windows Communication Foundation - "provides services to facilitate transactions with various distributed binding classes." (Troelsen: 5th Ed., p. 878)
- Windows Workflow Foundation - provides transactional support
- In ADO.NET can, of course, always write invokable-from-.NET stored procedures that incorporate transactions
- Key members of the ADO.NET Transaction object
- All vendor-specific transactions objects derive from
DBTransaction
- More importantly,
DBTransaction
implementsIDbTransaction
interface:
public interface IDbTransaction : IDisposable
{
IDbConnection Connection { get; }
IsolationLevel IsolationLevel { get; }
void Commit();
void Rollback();
} Connection
property returns a reference to the connection object that fired the transaction- Notes re:
IsolationLevel
- Contains enumerable values
- Default: transactions completely isolated until committed
- Notes re:
SqlTransaction
type- Also contains a
Save()
method, used to define save points - Passing in the name of the save point to the
Rollback()
method gives you a partial rollback
- Also contains a
- All vendor-specific transactions objects derive from
- Adding a method to a data access layer
- Method will invoke two or more SQL commands, but roll them into a single transaction
- After creating relevant command objects instantiate a transaction object, setting it to
null
- Inside a
try
block (& assuming we are using MSSS)…- Invoke
BeginTransaction()
method on connection object to instantiate the transaction variable - Syntax/example (assuming you have used
tx
as your transaction variable name &sqlCn
as your connection variable name):
tx = sqlCn.BeginTransaction();
- Set the
Transaction
property of your command objects to the just-defined transaction variable - Run the
ExecuteNonQuery()
methods on your command objects - Run the
Commit()
method on your transaction object
- Invoke
- Inside your
catch
block- Your
Commit()
command will of course not fire if any of theExecuteNonQuery()
methods throw an exception - Apart from whatever other exception handling you do be sure to run the
Rollback()
method of your transaction object
- Your
- General
- The Connected layer of ADO.NET
- Connecting to databases
- The Disconnected Layer
- General
- Relevant types of
System.Data
namespaceDataSet
DataTable
DataColumn
DataRow
DataView
DataRelation
- Allows you to operate on local copy of data
- "…data adapter objects function as a bridge between the client tier and a relational database." (Troelsen: 5th Ed., p. 885)
- Typical process
- Obtain
DataSet
, which is a client-side, in-memory copy of data - Manipulate the data in the
DataSet
- Push all data changes from
DataSet
to database - Note: opening & closing db connections are managed on your behalf by data adapter
- Obtain
- Variations
- Can build an entire
DataSet
off-line & push it up to a database - Can use a
DataSet
for off-line data handling, without ever connecting to a database
- Can build an entire
- Notes…
- With Windows Forms or WPF you use
- Data-binding
- Strongly-typed
DataSets
- Typically use LINQ to DataSet to manipulate data
- With Windows Forms or WPF you use
- Relevant types of
- The 'Data' types
DataSet
- General
DataSet
holds three strongly-typed collectionsDataTableCollection
DataRelationCollection
PropertyCollection
PropertyCollection
- Accessed via the
ExtendedProperties
property (use an indexer) - Platform for adding ad-hoc information via name/value pairs
- Syntax/example - to create:
myDataSet.ExtendedProperties["Company"] =
"Bluth Company"; - Syntax/example - to read:
// Inside a method…
foreach (System.Collections.DictionaryElement
de in myDataSet.ExtendedProperties)
{
Console.WriteLine("Key = {0}, Value = {1}",
de.Key, de.Value);
} - Note:
DataTable
&DataColumn
classes also have anExtendedProperties
property
- Accessed via the
- Key properties of the
DataSet
CaseSensitive
- Relates to case sensitivity of string comparisons
- Default:
false
DataSetName
- Friendly name
- Typically set as a constructor parameter
EnforceConstraints
(default:true
)HasErrors
(checks all rows of all tables inDataSet
)RemotingFormat
- Sets how
DataSet
should serialize its content - Default: XML (vs. binary)
- Sets how
- Key methods of the
DataSet
AcceptChanges()
- Commits all changes to
DataSet
- If
AcceptChanges()
has already been run commits changes only since that time
- Commits all changes to
Clear()
- blows out every row in everyDataTable
Clone()
- Clones structure of a
DataSet
DataTables
DataRelations
Constraints
- Does not copy data
- Clones structure of a
Copy()
- copies both structure & dataGetChanges()
- Returns a copy of the
DataSet
containing only those rows which have changed since loaded or sinceAcceptChanges()
was last run - Method is overloaded, so that you can specify which rows you want:
- New rows
- Modified rows
- Deleted rows
- All changed rows
- Returns a copy of the
HasChanges()
Merge()
(i.e., with anotherDataSet
)ReadXml()
- Apparently quite powerful
- Allows you to:
- Define structure of a
DataSet
object (based on XML schema??) - Populate the
DataSet
by reading from a stream
- Define structure of a
RejectChanges()
(i.e., effectively executes a roll-back on yourDataSet
)WriteXml()
- General
DataColumns
- General
- If constructing a
DataSet
from scratch createDataColumns
before creating aDataTable
- At very least must assign a data type to each column
- Properties
AllowDBNull
(default:true
)AutoIncrement, AutoIncrementSeed
, &AutoIncrementStep
(of course, typically used only with primary key column)Caption
(user-friendly name of a db column)ColumnMapping
- Comes into play when you invoke
DataSet.WriteXml()
- Determines XML-representation of column (i.e., element, attribute, content, or ignore)
- Comes into play when you invoke
ColumnName
(if not specified columns will be namedColumn1
,Column2
, etc.)DataType
DefaultValue
Expression
Ordinal
(position of the column in theColumns
collection)ReadOnly
(default:false
)Table
(returns the parentDataTable
)Unique
(i.e., values in column)
- If constructing a
- Building a
DataColumn
- When building a column that will act as the primary key set
ReadOnly
totrue
- When setting data type (usually within the
DataColumn
's ctor) use, say,typeof(string)
rather than simplystring
- When building a column that will act as the primary key set
- Enabling
Autoincrementing
fields (can set for primary key column) - Adding
DataColumn
objects to aDataTable
- Of course create the
DataTable
first - Syntax/example (assume columns are already defined):
// Within a procedure…
DataTable myTable = new DataTable("MyNewTable");
myTable.Columns.AddRange(new DataColumn[]
{ myCol1, myCol2, myCol3,… });
- Of course create the
- General
DataRows
- General
- Key members of
DataRow
typeHasErrors
- booleanGetColumnsInError()
GetColumnError()
- gives you error descriptionClearErrors()
- removes an error listing for a rowRowError
- use to set description of error for a given rowItemArray
- use to get or set all column values in row using an array of objectsRowState
- see TheRowState
property, belowTable
- gives you parentDataTable
AcceptChanges()
&RejectChanges()
- applies to all changes sinceAcceptChanges()
was last calledBeginEdit(), EndEdit()
, &CancelEdit()
Delete()
IsNull()
- applies to value in specified column in row
- Working with
DataRow
objects- Cannot create a
DataRow
directly (it has no public constructor) - Must invoke
NewRow()
method on aDataTable
object - Notes re:
NewRow()
method- Always gives you the next 'slot' in the
DataTable
- Creates the row but does not add the row to the table!!
- Syntax/example - for creating and then adding a new row to a table:
// Inside some method…
DataRow myRow = myDataTable.NewRow();
// Typically populate data here, then… myDataTable.Rows.Add(myRow);
- Always gives you the next 'slot' in the
- Use indexer (i.e., integer or column name string) to populate a
DataRow
- Syntax/example:
empRow = employeeTable.NewRow();
empRow["LastNm"] = "Fünke"
empRow["FirstNm"] = "Tobias"
- Cannot create a
- Key members of
- The
RowState
property- Reflects the state of a
DataRow
beforeAcceptChanges()
has been invoked on parentDataTable
-
DataRowState
enumerations:Added
Deleted
Detached
- Indicates a
DataRow
has been created but you have not yet invokedmyDataTbl.Rows.Add(myRow)
- State also exists if a
DataRow
has been removed from a collection
- Indicates a
Modified
Unchanged
- Significant feature of ADO.NET is that when you want to update database only modified rows are submitted
- Reflects the state of a
- The
DataRowVersion
property- Enumerations:
Current
Default
- Default
DataRowVersion
value - For
Added, Modified
, orDeleted
states default value isCurrent
- Default
Original
- Value from when value is first inserted into
DataRow
- Updated whenever
AcceptChanges()
is invoked
- Value from when value is first inserted into
Proposed
- indicatesBeginEdit()
has been called on a row
- Edit methods &
DataRowState
values- Calling
DataRow.BeginEdit()
and changing a row's value makes availableCurrent
valueProposed
value
- Calling
DataRow.CancelEdit()
deletesProposed
value - Calling
AcceptChanges()
- Same effect whether invoked from
DataTable
orDataRow
Original
value set ==Current
value
- Same effect whether invoked from
- Calling
DataRow.RejectChanges()
Proposed
value is discardedDataRowVersion
==Current
- Note: this all means that there are many occasions where certain versions are not available
- Calling
- A wise programmer will incorporate
try/catch
blocks when accessingDataRowState
- All in all, that a
DataRow
maintains three copies of data gives you a lot of programming flexibility
- Enumerations:
- General
DataTables
- General
- Many members of the class replicate those of
DataSet
- Significant members
CaseSensitive
- Relates to string comparisons
- Default:
false
ChildRelations
- a collection (if any exist)Constraints
Copy()
- gives you both schema & data, returning, of course, a newDataTable
DataSet
- can be emptyDefaultView
ParentRelations
PrimaryKey
- gets or sets an array ofDataColumns
RemotingFormat
TableName
- Setting the
PrimaryKey
- To account/allow for multi-column keys,
PrimaryKey
is a collection ofDataColumns
- Syntax/example (when you have already defined & added your primary key column(s):
myTable.PrimaryKey =
new DataColumn[] { myTable.Columns["MyPrimaryKeyColName"] };
- To account/allow for multi-column keys,
- Many members of the class replicate those of
- Syntax/example - adding
DataTables
to aDataSet
:
// Inside a procedure…
myDataSet.Tables.Add(myTable); - Obtaining data from a
DataSet
- Generally you loop through each
DataRow
- Within each
DataRow
you then loop through eachDataColumn
- Syntax/example - for looping through each
DataColumn
:
//Inside a method ("dt" is the DataTable variable)…
for (int curRow = 0; curRow < dt.Rows.Count; curRow++)
{
for (int curCol = 0; curCol < dt.Columns.Count; curCol++)
{
// Do something with the returned string value...
dt.Rows[curRow][curCol].ToString();
}
}
- Generally you loop through each
- Processing
DataTable
data usingDataTableReader
objects- Processing data - connected vs. disconnected layers (so far…)
- Connected layer
- You employ
DataReader
object - Call
Read()
method inside awhile
loop
- You employ
- Disconnected layer
- Use a
DataSet
object - Set up a series of iterations to drill into
DataTables, DataRows
, & thenDataColumns
- Use a
- Connected layer
- However,
DataTable
objects support a method calledCreateDataReader()
- Returns a
DataTableReader
object - Functionality of
DataTableReader
is essentially identical to that of aDataReader
object - When using this method you are still working on a disconnected basis
- "The major benefit of this approach is that you now use a single method to process data, regardless of which layer of ADO.NET you use to obtain it." (Troelsen: 5th Ed., p. 900)
- Very useful method for when you quickly want to push out all data in a table
- Returns a
- Processing data - connected vs. disconnected layers (so far…)
- Serializing
DataTable/DataSet
objects as XML- With
WriteXml()
you push data into either:- Local file
- Any
System.IO.Stream
-derived type
- Both
DataSets
&DataTables
also support:WriteXmlSchema()
ReadXmlSchema()
- With
WriteXmlSchema()
- You must, at a minimum, supply file name
- Default file location is, of course,
\bin\Debug
- With
- Serializing
DataTable/DataSet
objects in binary format- Binary format is useful when a
DataSet
object needs to be passed between machines - (XML files, unfortunately, can become quite bloated)
- Requirements
- Set
RemotingFormat
property toSerializationFormat.Binary
- Import requisite namespaces:
System.IO
System.Runtime.Serialization.Formatters.Binary
- Set
- Then use
BinaryFormatter
type - When running your
BinaryFormatter
'sDeserialize()
method remember to cast the result as aDataSet
- Syntax/example:
private static void SaveAndLoadAsBinary(DataSet myDataSet)
{
// Set binary serialization flag...
myDataSet.RemotingFormat = SerializationFormat.Binary;
// Save the DataSet as binary...
FileStream fs =
new FileStream("MyData.bin", FileMode.Create);
BinaryFormatter bFormat = new BinaryFormatter();
bFormat.Serialize(fs, myDataSet);
fs.Close();
// Clear out DataSet...
myDataSet.Clear();
// Load the DataSet from binary file...
fs = new FileStream("MyData.bin", FileMode.Open);
DataSet data = (DataSet)bFormat.Deserialize(fs);
}
- Binary format is useful when a
- General
- Binding
DataTable
objects to Windows Forms GUIs- Hydrating a
DataTable
from a genericList<T>
- See Troelsen: 5th Ed., pp. 904-7, for full example
- With a Windows
Form
object…- There are 2
partial class *.cs
files - Our code goes in the file containing the
Form's
constructor - (Constructor file is the one without the VS-generated code)
- There are 2
- Troelsen's example is probably not approach you would take in production, but …
- Create a Windows Form project & place a
DataGridView
control on the (main) form - Create, say, an
Employee
class within the project - Within the main
Form
add 2 form-wide variables:- A generic
List<T>
:
List<employee> listBluths = null;
- A
DataTable
:
DataTable BluthEmployeesTable = new DataTable();
- A generic
- In his example Troelsen then runs a
foreach
loop over hisList<T>:
to populate theDataTable
- From within
Form's
constructor:
// Bind data to form…
BluthmployeeGridView.DataSource = BluthEmployeesTable;
- Create a Windows Form project & place a
- Deleting rows from a
DataTable
- Put your code in a
try/catch
block! - Use
Select()
method of yourDataTable
to identify your row(s) - Note: the
Select()
method returns aDataRow
array - Syntax/example (ignoring the
try/catch
blocks):
private void btnFireEmployee_Click(object sender, EventArgs e)
{
// Find the row to delete…
DataRow[] rowToDelete = employeeTable.Select(string.Format("ID = {0}",
int.Parse(txtEmployeeToFire.Text)));
// Fire Bluth employee…
rowToDelete[0].Delete();
employeeTable.AcceptChanges();
}
- Put your code in a
- Selecting rows based on filter criteria
- Might well want to build your filter as a
string
& then pass thatstring
intoSelect()
- Note: when building a text filter string remember to include apostrophes!
- Syntax/example:
string filterStr = string.format("LastName = '{0}'", txtLastName.Text);
Select()
method overloaded to accept a wide range of filters- Filters are based on SQL syntax
- To sort results simply add column name(s) as 2nd parameter in
Select()
- Can additionally specify
ASC
(default) orDESC
after sort column name for sort order - Can include relational operators (>, <, etc.) in building filtering string
- Might well want to build your filter as a
- Updating rows within a
DataTable
- Use
Select()
to obtain rows you want to modify - Make the necessary modifications on the
DataRow[]
array - Modified
DataRows
do not have to be "put back" into yourDataTable
- Use
- Working with the
DataView
type- Directly analogous to a db View
- "In ADO.NET, the
DataView
type allow you to extract a subset of data programmatically from theDataTable
into a stand-alone object." (Troelsen: 5th Ed., p. 912) - Mechanics
- Pass a
DataTable
orDataView
object in as a constructor parameter - Set the desired
string
filter as theRowFilter
property
- Pass a
- You can then bind the various views to different GUI widgets… which is just a splendid thing to do
- In particular, you can bind a
DataView
object to a GUI just as you would bind aDataTable
- Hydrating a
- Connecting the disconnected layer
- Working with data adapters
- General
- A data adapter is the object used to retrieve data from & send data to a db
- Base class is
DbDataAdapter
; child classes areSqlDataAdapter, OracleDataAdapter
, etc. - Key members of
DbDataAdapter
classFill()
- Executes the SQL SELECT command specified by/in the
SelectCommand
property - Use to load data into a
DataSet
orDataTable
- Executes the SQL SELECT command specified by/in the
- The SQL commands that will fire when
Fill()
&Update()
methods are invokedSelectCommand
InsertCommand
UpdateCommand
DeleteCommand
Update()
- pushes modified data back to db
- Connecting a data adapter to a db
- You supply data adapter with connection object or connection string
- After that you never open or close a db connection when using a data adapter object!
- (Data adapter manages the connection for you)
- Connectivity flexibility
- You can dynamically change a data adapter's connection objects
- Ergo, you can connect to different databases on the fly
- In fact, you can even build a
DataSet
by mixing & matching from different dbs - even say across db vendors!
- Creating a data adapter
- Done on the client side
- Syntax/example (inside a client-side procedure):
// Define connection string…
string cnStr = "BlahBlah";
// Create DataSet…
DataSet ds = new DataSet("BluthEmployees");
// Instantiate data adapter…
SqlDataAdapter dAdapt =
new SqlDataAdapter("Select * from BluthEmployees", cnStr);
// Fill DataSet…
dAdapt.Fill(ds, "BluthEmployees"); - Note:
Fill()
method returns number of rows returned by your SQL query
- Mapping database names to friendly names
DataAdapter
has aTableMappings
property- Internal name:
DataTableMappingCollection
- This collection property is strongly typed for
System.Data.Common.DataTableMapping
objects
- Internal name:
- Must add
using System.Data.Common;
statement - Syntax/example:
// Inside some procedure (dAdapt = DataAdapter)…
DataTableMapping emplMap =
dAdapt.TableMappings.Add("f_name", "First Name");
emplMap.Add("l_name","Last Name");
- General
- Adding disconnected functionality to a DAL.dll
- Defining the initial class type
- Create a new class, calling it something like
employeesDALDisLayer
- This class should be in its own namespace (e.g.,
BluthCoDisconnectedLayer
) - Note: you do not create methods to open & close db connections (as is required with the connected layer)
- Create 2 class fields:
- A
string
variable for the connection string (initialize asstring.Empty
) - A data adapter field, initialized to
null
- A
- Create a custom constructor, which takes a connection string as a parameter
- Set the class field = constructor parameter
- Call a private method to configure the adapter
- Syntax/example:
namespace BluthCoDisconnectedLayer
{
public class EmployeeDALDisLayer
{
// Field data…
private string cnString = string.Empty;
private SqlDataAdapter dAdapt = null;
public EmployeeDALDisLayer(string connectionString)
{
cnString = connectionString;
// Configure the SqlDataAdapter [Defined below!!]…
ConfigureAdapter(out dAdapt);
}
}
}
- Create a new class, calling it something like
- Configuring the data adapter using the
SqlCommandBuilder
- First priority is to assign valid command objects to the following data adapter properties:
UpdateCommand
DeleteCommand
InsertCommand
- Based on knowledge so far would have to:
- Manually create 3 new command objects
- Declare appropriate parameter objects
- Add the parameters to the appropriate command objects via each object's
Parameters
property
- However, designer tools of various UIs simplify the process of creating these 3 commands
- Syntax/example:
// This is the method you call in DALDisLayer ctor [above]…
private void ConfigureAdapter(out SqlDataAdapter dAdapt)
{
// Create the adapter & set up the SELECT command…
dAdapt = new SqlDataAdapter("Select * from Employee", cnString);
// Set the remaining command objects at runtime using
// the SqlCommandBuilder…
SqlCommandBuilder builder = new SqlCommandBuilder(dAdapt);
} - The command builder type
- Creates values within
InsertCommand, UpdateCommand
, &DeleteCommand
based on initialSelectCommand
- Accomplishes this by reading the relevant database's schema/metadata whenever
Update()
is invoked (at runtime) - Drawback: this does require a round trip to the db
- To minimize this performance hit you want to call your command builder inside (well, via a call to a private method) your DALDisconnectedLayer class constructor
- In the above example note that we never use the
SqlCommandBuilder
object that we instantiate!! - We code this way because – behind the scenes – simply allocating & configuring that object configures the data adapter with our 3 command objects
- Creates values within
- Note: command builder objects will auto-generate your 3 command objects only under the following conditions:
- The SQL Select command retrieves data from a single table (i.e., there are no joins involved)
- That single table has a primary key
- Your Select command includes the primary key column(s)
- First priority is to assign valid command objects to the following data adapter properties:
- Creating a GetAll method
- Method must, of course, be
public
- Method should return a
DataTable
- Inside method:
- Create a local variable of type
DataTable
(passing in name of table as constructor's parameter) - Run
Fill()
method on class's private instance of data adapter, passing in your local instance of theDataTable
- Return that
DataTable
object
- Create a local variable of type
- Method must, of course, be
- Creating an Update method
- Method should return
void
- Pass in a
DataTable
object (typically calledmodifiedTable
) - Run
Update()
method on class's privateDataAdapter
object, passing inmodifiedTable
as a parameter
- Method should return
- Testing disconnected functionality
- Defining the initial class type
- Working with data adapters
- Multitabled dataset objects & data relationships
- General
- Prepping data adapters
- Building table relationships
- Updating database tables
- Navigating between related tables
- Windows Forms database designer tools
- General
- Visually designing a
DataGrid
view - The generated
app.config
file - Examining the strongly-typed
DataSet
- Examining the strongly-typed
DataTable
- Examining the strongly-typed
DataRow
- Examining the strongly-typed
DataAdapter
- Isolating strongly-typed database code into a class library
- General
- Viewing the generated code
- Selecting data with the generated code
- Inserting data with the generated code
- Deleting data with the generated code
- Invoking a stored procedure using the generated code
- Programming with LINQ to DataSet
- General
DataSet
extensions library- Obtaining a LINQ-compatible
DataTable
- The
DataRowExtensions.Field<T>()
extension method - Hydrating new
DataTables
from LINQ queries
- General
- The Entity Framework
- General
- EF introduced with .NET 3.5 SP 1
- "The overarching goal of EF is to allow you to interact with relational databases using an object model that maps directly to the business objects in your application." (Troelsen: 5th Ed., p. 951)
- EF & LINQ
- Entities - i.e., the business objects - are LINQ-aware out of the box
- EF runtime engine translates LINQ queries into SQL behind the scenes
- The role of the Entity Framework
- General
- Revisiting connected & disconnected layers of ADO.NET
- Both 'talk' in the language of databases (i.e., tables, rows, etc.), not business objects
- Obviously, strongly-typed
DataAdapters, DataTables
…- Do have a more OO feel to them
- Also enable LINQ to DataSet, which is a splashingly good feature
- LINQ to DataSet limitations
- LINQ queries are operations on the dataset - not on the db itself
- As such, you must still pay a lot of attention to the db structure - i.e., versus focusing on OO structure
- C# code can get very complex when handling more complicated db structures
- The Entity Framework
- Allows you to create a LINQ query that operates on the db & returns strongly-typed data
- If so desired, handles all SQL for you
- Augments, rather than replaces, connected & disconnected layers (though EF often removes the need to deal directly with those layers)
- Note: MSFT introduced LINQ to SQL with .NET 3.5, but now encourages reliance on EF
- Revisiting connected & disconnected layers of ADO.NET
- The role of entities
- "Entities are a conceptual model of a physical database that maps to your business domain." (Troelsen: 5th Ed., p. 953)
- This is specifically referred to as an Entity Data Model (EDM)
- "The EDM is a client-side set of classes that map to a physical database…" (Troelsen: 5th Ed., p. 953)
- One very nice feature of entity classes is the ability to rename columns as they are translated into properties
- Building blocks of the entity framework
- General
- Again, the EF API augments, rather than replaces, existing ADO.NET infrastructure
- EF requires an ADO.NET data provider to communicate with db
- EF-related types are in
System.Data.Entity.dll
assembly - There are several relevant namespaces within the assembly
- Object Services
- Object Services is the part of EF related to client-side entities - i.e., your business objects
- Capabilities:
- Tracks changes to each individual entity
- Manages relationships between entities
- Handles saving changes to db
- Allows creation of XML & binary data
- Object services responsible for handling any type that extends
EntityObject
base class
- The Entity Client
- The part of EF which interacts with ADO.NET data provider
- Responsibilities
- Connect to db
- Generate SQL statements from
- Modifications to entities themselves
- LINQ queries
- Shape data retrieved from db into entities
- Located in
System.Data.EntityClient
namespace - Types in
System.Data.EntityClient
namespaceEntityCommand, EntityConnection, EntityDataReader
, others- These, of course, mirror/mimic the classes in ADO.NET data providers
- All in all, classes connect EF concepts to data providers
- EntitySQL
- Vendor-agnostic version of SQL that works with entities
- EF maps EntitySQL to vendor-specific SQL automatically
- You can, however, tweak that mapping
- The
*.edmx
file (& friends)- Controls mapping of db tables & columns to entity names & property names
- This whole mapping process must be divided into 3:
- Conceptual model - defines entities & how they relate to each other
- Logical model - maps entities & relationships to DB tables, including any/all foreign key constraints
- Physical model - physical aspects of db, such as
- Storage
- Table schema
- Partitioning
- Indexing
- Details of each of the 3 models/layers are contained in an XML-based file
- The
*.edmx
file contents will in turn be used to generate 3 stand-alone XML files:- Conceptual model:
*.csdl
- Physical model:
*ssdl
- Mapping layer:
*.msl
- Conceptual model:
- Upon compilation the 3 XML files are turned into binaries
- The
ObjectContext
&ObjectSet<T>
classesObjectContext
class is inSystem.Data.Objects
namespace- A class that extends
ObjectContext
is created when you generate*.edmx
file- This class enables you to (indirectly) work with object services & entity client functionality
- This class will take the name of your entire database with the word "Entities" appended (e.g.,
MyDatabaseEntities
)
- Important members of
ObjectContext:
AcceptAllChanges()
- accepts changes within the object contextAddObject()
DeleteObject
- marks an object for deletionExecuteFunction<T>()
- use to execute a stored procedureExecuteStoreCommand()
- use to send an SQL command directly to dbGetObjectByKey()
SaveChanges()
- pushes updates to dbCommandTimeout
Connection
- read-only property which returns the connection stringSavingChanges
(event)
ObjectSet<T>
ObjectContext
is, in some ways, just a container class for entity objects- Entity objects are stored in a collection of type
ObjectSet<T>
- One such collection is created for each table in database
- Each collection is a property on the relevant
ObjectContext
object - The name of the property/collection = a pluralized version of the table name
- Each collection is a property on the relevant
- Example: you have a database called
BluthCo
which contains anEmployee
table- You will have an object called
BluthCoEntities
, which will be of typeObjectContext
BluthCoEntities
will have a property calledEmployees
(note plural)- The
Employees
property will encapsulate anObjectSet<Employee>
member variable
- You will have an object called
- Important members of
ObjectSet<T>
:AddObject()
CreateObject<T>()
DeleteObject
- marks an object for deletion
- Very often use LINQ to Entity queries against
ObjectSet<T>
collections
- Syntax/example:
using (BluthCoEntities context = new BluthCoEntities())
{
// Put Lindsey on the payroll…
context.Employees.AddObject(new Employee()
{ LastName = "Bluth", FirstName = "Lindsey" });
context.SaveChanges();
}
- Note: See Troelsen: 5th Ed., Figure 23-6, p. 960, for a good diagram of components of Entity Framework
- General
- General
- Building & analyzing an EDM
- Generating the *.edmx file
- Two approaches to creating an *.edmx file
- Domain first programming (introduced with .NET 4.0)
- Create your entities first
- Then create your database from that! (very cool)
- Create entities from existing database
- Domain first programming (introduced with .NET 4.0)
- Option 1: Command line utility,
EdmGen.exe
EdmGen.exe -?
will give you a list of all available options- Can use for domain first programming
- Option 2: Use VS graphical EDM designers
- Under select
- Suggested naming convention:
MyDbNameEDM.edmx
- Under
(assuming you are creating from a database)
- Under
- Umm… chose your data connection
- Be sure to check the checkbox!
- Note: the generated connection string contains a
metadata
flag which specifies the name of the 3 component XML files
- Select the tables, views, and/or stored procedures you want to include
- Two approaches to creating an *.edmx file
- Reshaping the entity data
- When using VS designer windows to create an
*.edmx
file you will be presented with a designer window - To access that window can also right-click on the edmx file in
- From on shortcut menu select
- Right-click on that window & select
- In
window…
- Can see the physical attributes of db under the 'Store' node
- Naming Convention: For
BluthCo
db the node will show as
- To rename any entity or its properties:
- Right-click on appropriate item within designer window
- Select from short-cut pop-up
- Change name in window
- When renaming an entity
- VS will automatically rename the Entity Set Name
- Example: if you change the name of your
Inventory
table toWidget
the Entity Set Name will be changed toWidgets
(plural) - Note: the Entity Set Name, in turn, is the name of the property that encapsulates the
ObjectSet<T>
member variable of theObjectContext
-derived class
- Remember to compile your app after reshaping entity! (This generates the requisite
*.csdl, *.msl
, &*.ssdl
files)
- When using VS designer windows to create an
- Viewing the mappings
- Access the via the short-cut menu from any item in the Designer Window
- Window will show physical layer on the left & the conceptual layer on the right
- Viewing the generated
*.edmx
file data- Steps
- Right-click on appropriate
*.edmx
in window - Select
- Easiest option is probably
- Right-click on appropriate
- Notes
- Under the
SSDL
layer…<Schema>
node specifies the name of the ADO.NET data provider that will be used to communicate with db (e.g.,System.Data.SqlClient)
<EntityType>
node contains details on each table & its columns
<edmx:ConceptualModels>
node contains all client-side entities
- Under the
- After compiling project…
- VS creates an
edmxResourcesToEmbed
sub-folder underobj\Debug
- That sub-folder will contain the 3 XML files
- VS creates an
- Steps
- Viewing the generated source code
- Open
window (under ) to see:
- Various entity classes
- Your 'Entities' class (i.e., of type
ObjectContext
)
- 'Designer.cs' file
- This will be named whatever you named your
*.edmx
file, plus.Designer.cs
- Visible in
*.edmx
file under the node of your - Note: no point in ever editing this because VS recreates the file with each compile
- Contains various constructors for your
ObjectContext
-derived class - Also contains code for each
ObjectSet<T>
property of yourObjectContext
-derived class - Note: although this code will show the availability of various
AddTo…
methods in theObjectContext
-derived class Troelsen (5th Ed., p. 971) recommends:- Not using these methods
- Instead, working directly with the exposed-via-property
ObjectSet<T>
members
- This will be named whatever you named your
- Property setters inside the individual entity classes
- Work partly via a call to static
StructuralObject.SetValidValue()
method of EF API - Also inform EF runtime that a property has changed (an event your
ObjectContext
must track) - Lastly, implements 2 partial methods
- Per C# custom, these are the methods prefixed with "On"
- Methods handle change notifications regarding properties
- Work partly via a call to static
- Open
window (under ) to see:
- Enhancing the generated source code
- All designer-generated classes are built as
partial
classes - Ergo, they can be augmented with additional
partial class
constructs - Can then easily add business-logic methods
- Specific suggestions:
- Provide implementations for
OnMyPropertyChanged()
&OnMyPropertyChanging()
methods - Override
ToString()
method, in particular to handle absence of data in nullable fields - Syntax/example:
public partial class Employee
{
public override string ToString()
{
return string.Format("[FirstName: {0}; LastName: {1}]",
this.FirstName ?? "**No Name**", this.LastName);
}
partial void OnFirstNameChanging(global::System.String value)
{ // Some code… }
partial void OnFirstNameChanged()
{ // Some code… }
} - See The
??
operator (provides a default value for a nullable field )
- Provide implementations for
- All designer-generated classes are built as
- Generating the *.edmx file
- Programming against the conceptual model
- General
- Deleting a record
- Updating a record
- Querying with LINQ to Entities
- Querying with Entity SQL
- Working with the Entity Client Data Reader object
- Reworking a DAL *.dll for entities
- General
- Mapping the stored procedure
- Navigation properties
- Using navigation properties within LINQ to Entity queries
- Invoking a stored procedure
- Data binding entities to Windows Forms GUIs
- General
- Adding the data binding code
- General
- The Connected Layer
- LINQ to XML
- Windows Communication Foundation
- Distributed computing APIs
- General
- WCF introduced with .NET 3.0
- A distributed system generally implies at least 2 networked computers
- However, really only implies the exchange of data between 2 executables, even if on same machine
- Primary consideration(s) when building a distributed sytem -
- Will it be used exclusively in-house?
- Or, will external users need access?
- With in-house systems generally you can…
- Guarantee same
- Operating system
- Programming framework (i.e., .NET vs. COM vs. Java)
- Leverage existing security systems
- Guarantee same
- Externally-facing systems
- Cannot guarantee what you can with in-house systems
- In fact, if you face various operating systems or platforms in-house you are essentially forced to build your systems as if externally-facing
- Distributed Component Object Model (DCOM)
- The "remoting API of choice for Microsoft-centric development" prior to .NET (Troelsen: 5th Ed., p. 1014)
- Relied on COM objects & system registry
- Was labor-intensive
- Advantages
- Allowed for local transparency of components
- Physical location of host not hard-coded in client
- Instead, location recorded in registry
- This allowed code base to remain neutral vis-à-vis location
- Allowed for local transparency of components
- Disadvantages
- In practice it was Windows-centric
- Ergo, really only suited for in-house apps
- "DCOM alone did little more than define a way to establish a communication channel between two pieces of COM-based software." (Troelsen: 5th Ed., p. 1015)
- With advent of .NET DCOM should be considered a legacy technology
- COM+/Enterprise service
- COM+ was originally named Microsoft Transaction Server (MTS)
- Advances beyond DCOM
- Transaction management
- Object lifetime management
- Pooling services
- Role-based security system
- Loosely-coupled event model
- Others
- Notes:
- All of these features are/were available out-of-the-box (again, with DCOM much had to be coded by hand)
- All features could be set simply by checking boxes in appropriate administrative tools
- Since 1st release of .NET can use
System.EnterpriseServices
namespace to:- Build managed libraries that can be installed into COM+ runtime
- These libraries can access same set of services as a traditional COM server
- Using .NET to build COM+ services themselves
- Cannot be done using WCF
- Use
System.EnterpriseServices
to do so
- Whether the code is written in .NET or in COM, a COM+-aware library is called a serviced component
- COM+ summary
- Still in use
- However, a Windows-only solution
- Ergo, would only use COM+ for
- In-house systems
- A "back-end service indirectly manipulated by more agnostic front ends (e.g., a public website that makes calls on serviced components…" (Troelsen: 5th Ed., p. 1015)
- Microsoft Message Queuing (MSMQ)
- Designed for message data
- Designed to handle interruptions in traffic, which can be due to
- Server issues
- Offline databases
- Lost network connections
- Also designed for queuing data, which is where message data is held for delivery at a later time
- Initial incarnation was package of "low-level C-based APIs and COM objects." (Troelsen: 5th Ed., p. 1015)
- In .NET use
System.Messaging
namespace to work with MSMQ - MSMQ & COM+
- Although somewhat simplified, COM+ contains MSMQ functionality
- COM+ does so via something called Queued Components (QC)
- If working in .NET QC is available via
System.EnterpriseServices
- MSMQ still in use
- .NET remoting
- From the original .NET framework, this is the .NET component that rendered DCOM obsolete
- Found in
System.Runtime.Remoting
namespace - Allows multiple computers to distribute objects if the relevant apps are .NET
- Features & advantages
- Connection settings placed in client & server
*.config
files - With an all-.NET system data can
- Be exchanged in binary format (this helps performance)
- Remain in CTS
- Connection settings placed in client & server
- Mono allows you to work across different operating systems
- Cannot, however, talk to other platforms (e.g., Java)
- XML web services
- General
- Obviously, system & platform agnostic
- As name would indicate, uses standard web protocols
- Available since initial release of .NET platform via
System.Web.Services
namespace - Utilizing
- When creating host generally no more complicated than applying
[WebMethod]
attribute to your public methods - On client side VS 2010 allows access to web services very easily
- When creating host generally no more complicated than applying
- Because of HTTP & XML you can easily run into performance issues
- For internal systems better off using TCP-based protocol
- A .NET web service example
- In VS use the ASP.NET Web Service project template (
)
- Sets up commonly-used directory structure
- Also creates some initial files to represent the web service
- A barest-of-bones web service
- Can create using a text editor
- Syntax/example (saved as an *.asmx file):
<%@ WebService Language="C#" Class="HelloWebService" %>
using System;
using System.Web.Services;
public class HelloWebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
} - Note: runtime will automatically encode return type into XML
- Proxies
- A "proxy is a class that encapsulates the low-level details of communicating with another object…" (Troelsen: 5th Ed., p. 1018)
- When building a client you want to create & include a proxy to communicate with the web service
- Options for building
- 1) Use command-line tool
wsdl.exe
, which gives you full control over how the proxy is generated - 2) In VS use
- Both options generate a client-side
*.config
file
- 1) Use command-line tool
- In sum, while you can build a traditional web service in .NET, since .NET 3.0 you are better off utilizing WCF
- In VS use the ASP.NET Web Service project template (
)
- Web service standards
- In early days web service implementations varied by vendor
- A number of current standards, collectively called WS-*
- Authored by
- Areas covered
- Security
- Attachments
- Description of web services - Web Service Description Language (WSDL)
- Policies
- SOAP formats
- Others
- MSFT's implementation of WS-*
- Implemented via the Web Services Enhancement (WSE) toolkit
- WSE is a free download
- Creating WCF web services
- Does not require use of WSE toolkit
- Simply specifying HTTP-based binding gives you adherence to WS-* standards
- General
- Named pipes, sockets, and P2P
- These are low-level APIs
- Generally give you better performance (where you can use them)
- When your apps will run on the same machine use the
System.IO.Pipes
namespace - When you need control over network access use one of these namespaces
System.Net.Sockets
System.Net.PeerToPeer
- General
- Role of WCF
- General
- Prior to WCF, obviously faced a number of APIs
- WCF creates a single API for a number of previously-diverse APIs
- Namespace:
System.ServiceModel
- WCF features
- Integration & interoperability of diverse APIs
- Support for both strongly typed and untyped messages
- Support for several bindings
- Raw HTTP
- TCP
- MSMQ
- Named pipes
- Support for WS-*
- Fully-integrated security model, including
- Windows/.NET protocols
- Neutral-security techniques from WS-*
- Support for
- Session-like state management techniques
- One-way or stateless messages
- Addtitionally
- Tracing & logging facilities
- Performance counters
- Publish-and-subscribe event model
- Transactional support
- Service-Oriented Architecture (SOA)
- General
- WCF based on SOA design principles
- "Simply put, SOA is a way to design a distributed system where several autonomous services work in conjuction by passing messages across boundaries… using well-defined interfaces." (Troelsen: 5th Ed., p. 1021)
- You use .NET interface types for SOA interfaces
- Tenets
- Tenet 1: Boundaries are explicit
- Enforced via interfaces
- Only way a client can communicate with service is through the service's interface
- Tenet 2: Services are autonomous
- Phrase used is autonomous entities
- A service should be autonomous in terms of
- Version
- Deployment
- Installation issues
- It is ok to add new interfaces/functionality to a service
- Existing interfaces, however, should never be changed
- Tenet 3: Services communicate via contract, not implementation
- Related to reliance on interfaces
- Implementation details, such as underlying platform, should be immaterial to caller
- Data contracts
- Must be utilized whenever a service returns custom complex types
- Data contracts allows callers to map returned types to caller's own data structures
- Tenet 4: Service compatibility is based on policy
- Can generate a WSDL document using
- A service's interface
- Client choice of binding
- WSDL alone, however, does not tell you everything you need to know about a service
- SOA allows you to declare policies
- These can specify things like security requirements
- Interfaces specify the low-level syntax of your service
- Policies allow you to specify
- How interfaces work
- How interfaces need to be invoked
- Can generate a WSDL document using
- Tenet 1: Boundaries are explicit
- General
- WCF: The bottom line
- There may be cases where you need to use older APIs like COM+
- As a rule, though, you want WCF to be your distributed-app-building approach of choice
- WCF relies on
- XML-based configuration files
- .NET attributes
- Proxy generation utilities
- General
- Core WCF assemblies & namespaces
- Assemblies
System.Runtime.Serialization.dll
System.ServiceModel.dll
- Namespaces
System.Runtime.Serialization
System.ServiceModel
- Primary WCF namespace
- Contains
- Binding & hosting types
- Security & transaction types
System.ServiceModel.Configuration
(for types that give access in code to config files)System.ServiceModel.Description
- Also used for working with config files
- Provides objects for
- Addresses
- Bindings
- Contracts
System.ServiceModel.MsmqIntegration
(for working with MSMQ)System.ServiceModel.Security
- Cardspace API
- Types related to this are in the
System.IdentityModel.dll
- Cardspace is used to handle digital identies within WCF
- Types related to this are in the
- Assemblies
- VS WCF project templates
- General
- When building WCF apps you generally create 3 assemblies
- One of those assemblies is the service itself
- You can create a service by working with a standard Class Library project
- Typically, though, you work with a WCF Service Library project
- Automatically sets your required references
- Also creates an
App.config
(which, of course, is typically done for*.exe
files, not for*.dll
assemblies) - Downside: gives you a lot of boiler-plate code, which Troelsen says you often delete (Troelsen: 5th Ed., p. 1024)
- Debugging & the
app.config
file- Whenever you run or debug a WCF Service Library project VS launches the WCF Test Client application (
WcfTestClient.exe
) WcfTestClient.exe
reads yourapp.config
file to test your client assembly
- Whenever you run or debug a WCF Service Library project VS launches the WCF Test Client application (
- Other WCF project templates
- One template integrates WCF with Windows Workflow Foundation (WF)
- One template is used to build an RSS feed
- The WCF Service website project template
- Found via
- Useful when you know from the outset that you will be using web service-based protocols (e.g., versus, say, TCP)
- This project template will automatically create requisite
- Internet Information Services (IIS) directories
Web.config
file for your HTTP connections*.svc
file
- Only use this template, though, if you are sure you will be restricted to HTTP protocol
- General
- Composition of a WCF application
- The assemblies
- WCF Service assembly
- Contains the functionality you want to expose to callers
- Exposure done via WCF interface contracts
- Interfaces contain attributes, which control how
- Data types are represented
- WCF interacts with exposed types
- Other issues
- WCF Service host
- Can be any .NET executable
- The executable - a Windows Form app, WPF app, etc. - is what exposes the service
- Host utilizes
ServiceHost
type- Optionally a
*.config
file
- Note: can also use Windows Activation Service (WAS) as your host
- WCF client
- Can be any .NET app
- As with the host you use a
*.config
file, though here you are setting client-side plumbing - Note: if you build your service using HTTP-based bindings the client does not even have to be a .NET app (could be Java)
- WCF Service assembly
- The client- & server-side
app.config
files- Neither are technically required
- Can put all settings in code
- Disadvantage - must recompile assemblies if you change plumbing decisions
- Advantage - can use if-tests to establish your settings, giving you dyamic flexibility
- The assemblies
- ABCs of WCF
- General
- Address
- Location of service typically stored in
app.config
file - If address is hard-coded use a
System.Uri
type
- Location of service typically stored in
- Binding
- Network protocols
- Encoding mechanisms
- Transport layer
- Contract - i.e., a description of each method exposed by WCF service
- Address
- WCF contracts
- Generally 1st step of building a WCF service is to define contracts
- Contracts are represented & implemented by .NET interface types
- Nomenclature
- Service contracts - interfaces that represent WCF contracts
- Service types - the classes or types implementing these interfaces
- Attributes
- Used across service contracts
- Most common of these attributes found in
System.ServiceModel
namespace - If the members of the contract (i.e., methods specified by interfaces) deal only with simple data types you only need 2 attributes
[ServiceContract]
[OperationContract]
- For more complex contracts/methods
System.Runtime.Serialization
namespace (inSystem.Runtime.Serialization.dll
)- Available attributes
[DataMember]
[DataContract]
- Others
- Note: technically, you do not need to use/create interfaces
- Can apply the attributes directly to the public members of your WCF Service assembly
- However, Troelson says using .NET interfaces for your WCF contracts should be considered a best practice (Troelsen: 5th Ed., p. 1027)
- WCF bindings
- General
- Typically do this after you build your contract(s)
- All WCF hosts must specify the bindings callers may use to access the service
- WCF ships with any number of out-of-the-box bindings
- Can also extend the
CustomBinding
type
- HTTP-based bindings
- Relevant classes are in
System.ServiceModel
namespace - Bindings can be declared either in code or as XML attributes within your
*.config
files - Binding classes / binding elements
BasicHttpBinding
/<basicHttpBinding>
- Use for a WS-Basic Profile-conformant (WS-I Basic Profile 1.1) service
- Uses Text/XML as default message encoding
- Use if you need to provide backward compatability with ASP.NET web services
WSHttpBinding
/<wsHttpBinding>
- Supplies more services than
BasicHttpBinding
- Supports a subset of WS-* dealing with
- Transactions
- Reliable messaging
- WS-Addressing
- Can also handle Message Transmission Optimization Mechanism (MTOM), which is an encoding mechanism for binary data
- Supplies more services than
WSDualHttpBinding
/<wsDualHttpBinding>
- Use when you will employ duplex contracts (i.e., 2-way messaging)
- Allows you to hook into WCF's publish/subscribe event model
- Only security supported is SOAP
- Requires reliable messaging
WSFederationHttpBinding
/<wsFederationHttpBinding>
- Supports WS-Federation protocol
- This protocol is for authenicating & authorizing users who are all members of a federation
- Supports the following specifications
- WS-Trust
- WS-Security
- WS-SecureConversations
- Note: these specifications are all handled by the WCF CardSpace APIs
- Relevant classes are in
- TCP-based bindings
- Can use these when all machines, both client & host, are
- Windows
- .NET 4.0+
- Big benefit is that all data is encoded as binary, rather than as XML
- Binding classes / binding elements
NetNamePipeBinding
/<netNamePipeBinding>
- Use when client(s) & host(s) are on the same machine
- Supports
- Transactions
- Reliable sessions
- Secure communications
NetPeerTcpBinding
/<netPeerTcpBinding>
- for P2P appsNetTcpBinding
/<netTcpBinding>
- For cross-machine communication
- Supports
- Transactions
- Reliable sessions
- Secure communications
- Can use these when all machines, both client & host, are
- MSMQ-based bindings
- Use if you need to integrate with MSMQ server
- Binding classes / binding elements
MsmqIntegratedBinding
/<msmqIntegratedBinding>
- Use for back & forth messaging with MSMQ apps
- Works whether these apps use
- COM
- C++
- Types from the
System.Messaging
namespace
NetMsmqBinding
/<netMsmqBinding>
- Use when when all parties involved are .NET
- General
- WCF addresses
- Can be hard-coded (using
System.Uri
type) or entered in a*.config
file - Components
Scheme
- the transport protocol, e.g. HTTPMachineName
- use fully-qualified domain namePort
- Frequently optional
- For HTTP the default is 80
Path
- General template:
Scheme://<MachineName>[:Port]/Path
(again,Port
often omitted) - Examples
http://localhost:8080/MyWcfService
(again, you can omit the port if you are using the default value of80
)net.tcp://localhost:8080/MyWcfService
(for bothNetTcpBinding
orNetPeerTcpBinding
)net.msmq://localhost/private$/MyPrivateQ
- MSMQ bindings are a little different because if working on a local machine you can use public or private queues
- Also, port numbers are meaningless for MSMQ
net.pipe://localhost/MyWcfService
- Multiple addresses
- Can readily be done within a
*.config
file - Unique addresses are specified with
<endpoint>
elements - Supplying multiple addresses gives callers the option of which protocol to use when connecting with the service
- Can readily be done within a
- Can be hard-coded (using
- General
- Building the 3 WCF components - basics
- Building a WCF service
- General
- Can start by creating a run-of-the-mill class library
- Suggested naming conventions
- The class library:
MyServiceLib
- Any given class:
MyService
- The class library:
- Further, make sure to
- Add reference to
System.ServiceModel.dll
- Specify
using System.ServiceModel;
in the class
- Add reference to
- Suggested naming conventions
- Then create an interface that will serve as a WCF service contract
- Make sure the interface is public
- Adorn the interface itself with the
[ServiceContract]
attribute - Adorn each method that you want exposed through the WCF runtime with the
[OperationContract]
attribute
- Lastly, have your class implement the interface
- Note: Consider a putting something in the base class's constructor to emit a message indicating that the service has been 'turned on'
- Can start by creating a run-of-the-mill class library
- The
[ServiceContract]
attribute- Two main properties that can be set
Name
- Use to specify name of the service
- If not used the name = class name
Namespace
- Use to set XML namespace
- If not specified defaults to
http://tempuri.org
- You need to set a namespace if you will send or receive any custom data types
- Typically use the URI of the service's point of origin
- Syntax/example:
[ServiceContract(Namespace = "http://RCPConsulting.biz")]
public interface IMyInterface
{
// Specifics…
}
- "If you use a web service-specific binding, you use these values to define the
<portType>
elements of the related WSDL document." (Troelsen: 5th Ed., p. 1034)
- Other properties
CallbackContract
- Use to specify whether callback functionality is required for two-way messaging
- This, of course, is for duplex bindings
ConfigurationName
- Specifies where to find the service element in an
app.config
file - Default = name of the service implementation class
- Specifies where to find the service element in an
ProtectionLevel
- Sets how endpoints expose the contract
- In particular, sets whether contract requires either or both of
- Encryption
- Digital signatures
SessionMode
- Specifies sessions
- Options
- Allowed
- Not allowed
- Required
- Two main properties that can be set
- The
[OperationContract]
attribute- Must be applied to methods you want to expose in a WCF service
- Properties
AsyncPattern
- Use to flag that the method is implemented asynchoronously
- If that is the case the service will sport a
Begin/End
method pair - If utilizing this you wind up off-loading the processing to a separate server-side thread
- Note: this is not the same as calling the method asynchronously
IsInitiating
- specifies whether the method can be the initial operation in a sessionIsOneWay
- Use to flag that the method takes a single input message
- Note: in this case there will be nothing returned
IsTerminating
- flags that the WCF runtime should (attempt to) terminate the session after the method runs
- Note: property/value pairs might well be ignored based on binding the client selects
- Service types as operational contracts
- Again, you can omit creating interfaces & apply attributes directly to the relevant service class & its methods
- However, if you omit the interface you lose the…
- Benefits of polymorphism
- Ability to create new contracts by deriving from existing contract interfaces
- General
- Hosting a WCF service
- General
- If you are going to use an
app.config
file this is the assembly in which you insert it - Remember to add a reference to the your service library, & the appropriate
using
statement for that namespace
- If you are going to use an
- Establishing the ABCs with an
App.config
file- Steps
- Within the
app.config
file define the service's endpoint(s) - Within code use
ServiceHost
types to hook service types to relevant endpoint(s) - Keep your service up and running!
- Note: This last step is not needed if you host your service via
- Windows service
- IIS
- Within the
- Endpoints
- Represent
- Address
- Binding
- Contract
- Inside an
app.config
file<system.ServiceModel>
is the container element for all WCF-related settings- Next child is the
<services>
element - Next child is a
<service>
element- Optionally utilize the
name
attribute inside the<service>
element (for the friendly name) - Value of the
name
attribute should be the assembly-qualified name of the service
- Optionally utilize the
- Last child is the
<endpoint>
element, which should contain at least the following attributes:address
binding
contract
- for the assembly-qualified name of the service interface/contract
- Represent
- Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServiceLib.MyService">
<endpoint address="http://localhost:8080/MyService"
binding="basicHttpBinding"
contract="MyServiceLib.IMySvcContract" />
closing tags…
- Steps
- Coding against the
ServiceHost
type- Note: may have to run VS in administrator mode for the service to work
- When the executible that hosts the service starts up have it
- Create an instance of the
ServiceHost
type - Within that constructor identify which service is being hosted
- Create an instance of the
- "At runtime, this object will automatically read the data within the scope of the
<system.serviceModel>
element of the host's*.config
file to determine the correct address, binding, and contract." (Troelsen: 5th Ed., p. 1038) - Syntax/example:
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(MyService)))
{
// Open the host and listen for incoming messages…
serviceHost.Open();
// Perhaps generate user messages about svc being ready…
}
- Specifying base addresses
- If you want to specify your base address in code
- Add an array of
System.Uri
types as an argument within the constructor method ofServiceHost
- If you do this you must make an adjustment to your
app.config
file - Syntax/example (constructor invocation):
using (ServiceHost serviceHost =
new ServiceHost(typeof(MyService),
new Uri[]{new Uri("http://localhost:8080/MyService")})) - Syntax/example (modified
app.config
file:
<endpoint address = ""
binding="basicHttpBinding"
contract="MyServiceLib.IMySvcContract" />
- Add an array of
- You can also set up your
app.config
file to handle multiple base addresses - This approach has an added advantage - as
app.config
file gets more complex you would otherwise have to repeat your base addess(es) - Steps
- Give
address
attribute in<endpoint>
an empty value - Put all of the base addresses in their own section (this is where
<endpoint>
will get the value foraddress
) - Specifically, following the
<endpoint>
element add a<host>
element - Inside the
<host>
element add a<baseAddresses>
element - Inside the
<baseAddresses>
element include<add>
elements - Syntax/example:
…
<endpoint address=""…/>
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/MyService" />
closing tags…
- Give
- If you want to specify your base address in code
- Details of the
ServiceHost
type- You only use this type if specifically creating an executable to host your service
ServiceHost
is created for you if you use IIS to expose your service- Generally, you use
app.config
file to set most details of theServiceHost
- However, you can do a lot through code
- Properties
Authorization
BaseAddresses
- gives you a list of registered base addressesCloseTimeout
&OpenTimeout
- get/set time for the service to close down/start upCredentials
State
- Gives you a
CommunicationState
enum - Values are open, closed, created
- Gives you a
- Methods
AddDefaultEndpoint()
- Method is new as of .NET 4.0
- Can add pre-built endpoints found in the framework
AddServiceEndpoint()
BeginOpen(), BeginClose()
&EndOpen(), EndClose()
- These methods work on the
ServiceHost
object asynchronously - These use standard .NET asynchoronous delegate syntax
- These methods work on the
Open
&Close
- these communicate with theServiceHost
type synchronously
System.ServiceModel.Description
namespace- Use this to get information about a service model in code
- Syntax/example:
static void DisplayHostInfo(ServiceHost host)
{
foreach (System.ServiceModel.Description.ServiceEndpoint se
in host.Description.Endpoints)
{
Console.WriteLine("Address: {0}", se.Address);
Console.WriteLine("Binding: {0}", se.Binding);
Console.WriteLine("Contract: {0}", se.Contract.Name);
}
}
- Details of the
<system.serviceModel>
element (i.e., available sub-elements, all of which should be used as container elements)<behaviors>
- WCF sports a number of endpoint & service behaviors
- Behaviors allow you further control on how a host, service, or client works
<bindings>
- Use to- Fine-tune WCF-supplied bindings
- Specify custom bindings on the host
<client>
- Used by a client to contain a list of endpoints for connecting to a service<comContracts>
- Use to contain COM contracts for WCF/COM communication<commonBehaviors>
- Can only be used within a
machine.config
file - Defines all behaviors use by each WCF service on that particular machine
- Can only be used within a
<diagnostics>
- Use to contain settings for WCF diagnostics
- Available diagnostics
- Enable/disable tracing
- Performance counters
- WMI provider
- Can also add custom message filters
<services>
- Collection of WCF services available via the host
- Enabling metadata exchange
- Two options for creating the requisite proxy type on the client side
- Command-line tool
svcutil.exe
- In VS use
- Command-line tool
- Before a proxy can be created, though, these tools must be able to obtain
- Format of the WCF service interfaces
- Service's defined data contracts (i.e., method names & signatures)
- Metadata exchange (MEX)
- This is a WCF service behavior
- Enables you to adjust how the WCF runtime handles your service
- Any set of activities a service can subscribe to is contained in
<behavior>
element- You can build your own behavior(s)
- WCF also provides a number of behaviors out of the box
- MEX works by intercepting any metadata requests sent via HTTP GET
- Enabling MEX
- Disabled by default
- MEX must first be enabled if you want VS or
svcutil.exe
to automate the process of creating a client-side proxy*.config
file - Steps
- Inside host
app.config
file add an<endpoint>
element for MEX (see example below for attribute/value pairs) - Define a behavior that will allow HTTP GET access
- The gets handled in a
<behavior>
element - Use
name
attribute of the element, giving it a value such asMySvcMEXBehavior
- The
<behavior>
element should be nested inside a<serviceBehaviors>
element, which in turn should be nested inside a<behaviors>
element - The
<behaviors>
et. al. elements should follow the closing</services>
tag/element - Lastly, inside the
<behavior>
element you include a<serviceMetadata httpGetEnable="true" />
element (to enable MEX)
- The gets handled in a
- Link the MEX behavior to your service
- Use the value you assigned
name
attribute of your<service>
element (e.g., MySvcMEXBehavior) - Use this same name as the value of the
behaviorConfiguration
attribute of your opening<service>
element
- Use the value you assigned
- Add a
<host>
element as a sibling of your<endpoint>
element(s)- This will define the base address of the service
- This is where MEX finds the locations of the types to describe
- Note: the last step can be omitted if you include a
System.Uri
type to pass in the base address as a parameter in yourServiceHost
constructor
- Inside host
- Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServiceLib.MyService"
behaviorConfiguration="MySvcMEXBehavior">
<endpoint address=""
binding="basicHttpBinding"
contract="MyServiceLib.IMyService" />
<!-- MEX endpoint -->
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<!-- So MEX can find address of the service -->
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/MyService"/>
</baseAddresses>
</host>
</service>
</services>
<!-- MEX Behavior definition -->
<behaviors>
<serviceBehaviors>
<behavior name="MySvcMEXBehavior" >
<serviceMetadata httpGetEnabled="true"/>
closing tags…
- Testing/viewing your data contract
- Once you launch a service via its host app type the url of the service into any browser
- Can view the WSDL contract by clicking on the link which appears at the top of the web page
- Two options for creating the requisite proxy type on the client side
- General
- Building a WCF client app
- Generating proxy code using
svcutil.exe
- Builds both proxy code & client-side
*.config
file - How to use
- Note: the service must be running
- Set service's endpoint (e.g.,
http://localhost:8080/MySvc
as 1st parameter) - Then use 2 flags
/out
for proxy class (e.g.,/out:myProxy.cs
)/config
for name of client-side file (e.g.,/config:app.config
)
- The proxy file will contain…
- Client-side representations of service's contract interfaces
- A new class, named following the protocol
MyServiceClient
, which is the proxy class itself- The proxy class will
- Derive from
System.ServiceModel.ClientBase<T>
, where typeT
is the service contract/interface - Implement the service's contract/interface directly
- Derive from
- The proxy class will also contain
- Several custom constructors
- All contract/interface-specified methods, each of which implements an inherited (i.e., from
base
)Channel
property invoking the appropriate service method
- The proxy class will
- Syntax/example:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute
("System.ServiceModel", "4.0.0.0")]
public partial class EightBallClient :
System.ServiceModel.ClientBase<IEightBall>,
IEightBall
{
// Generated constructors omitted…
public string ObtainAnswerToQuestion(string userQuestion)
{
return base.Channel.ObtainAnswerToQuestion(userQuestion);
}
}
- The generated
App.config
file- This will control how client connects to service
- That connection is (of course) governed via the
<endpoint>
element - Syntax/example:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<!-- bindings and contained tags -->
<client>
<endpoint address="http://localhost:8080/MagicEightBallService"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IEightBall"
contract="IEightBall"
name="BasicHttpBinding_IEightBall" />
closing tags…
- Builds both proxy code & client-side
- Generating proxy code using VS 2010
- VS does not give you as many options as
svcutil.exe
- To create a client proxy using VS, however, select
- Supply service URI
- You should then see some descriptive information
- After clicking
VS automatically
- References WCF assemblies
- Creates a namespace
- Default name is
ServiceReference1
- This namespace is nested inside main namespace to avoid name clashes
- Default name is
- When programming against a service you must
- Add a
using
statement for the new namespace - Wrap you operative code inside a
using
statement - Syntax/example:
using System.ServiceModel;
using SomeServiceClient.ServiceReference1;
namespace SomeServiceClient
{
class Program
{
static void Main(string[] args)
{
using (SomeClient client = new SomeClient())
{ // Op code here… }
}
}
}
- Add a
- VS does not give you as many options as
- Configuring TCP-based binding
- To change a service's binding from http to TCP you only need to make changes in
- Host
app.config
file - Client
app.config
file
- Host
- Remember to assign a unique port (say, 8090)
- Things you can probably remove
- MEX settings from host
app.config
file (if you know there is a proxy built on the client side) <bindings>
elements (& children) on client side - if you are happy with default TCP settings
- MEX settings from host
- To change a service's binding from http to TCP you only need to make changes in
- Generating proxy code using
- Building a WCF service
- Building the 3 WCF components - advanced
- Simplifying configuration settings with WCF 4.0
- General
- WCF configuration files can be verbose & complex
- There can also be a lot of redunancies across bindings
- .NET 4.0 introduces some simplifications, though
- Default endpoints in WCF 4.0
- Prior to .NET 4.0 the CLR would throw a runtime exception if you had not specified at least 1
<endpoint>
in yourapp.config
file - Now, though, every WCF service is given a default endpoint
- Defaults are located in
machine.config
file - Within
<system.serviceModel>
element there is a<protocolMapping>
element - The
<protocolMapping>
elements contains<add scheme="http" binding="basicHttpBinding" />
, etc. elements
- Defaults are located in
- Net, net, you can build an
app.config
file specifying only:<service>
element (&, of course,name
attribute)<add baseAddress="some address…": />
element (inside, of course,<baseAddresses>
element)
- Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MyServiceLib.MyService" >
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/MyService"/>
closing tags… - Note: MEX not enabled in this example
- Prior to .NET 4.0 the CLR would throw a runtime exception if you had not specified at least 1
- Exposing a single WCF service using multiple bindings
- Supplying multiple bindings is a huge advantage of WCF
- Prior to WCF this was effectively immpossible because of the different APIs used for any given binding type
- With multiple bindings you can
- Provide a fast connection to internal clients via TCP
- Still give access to external clients via HTTP
- Prior to .NET 4.0 you supplied multiple bindings via multiple endpoints
- As of .NET 4.0 you simply drop another
<add baseAddress="…">
inside the<baseAddresses>
element - How the client selects which endpoint to use
- "When you generate a client-side proxy,[sic] the Add Service reference tool will give each exposed endpoint a string name in the client side
*.config
file" (Troelsen: 5th Ed., p. 1053) - You then pass that string name into proxy's constructor
- "When you generate a client-side proxy,[sic] the Add Service reference tool will give each exposed endpoint a string name in the client side
- Before this, however, you must…
- Reestablish the MEX for the hosting configuration file
- Modify the settings of the default binding
- Changing settings for a WCF binding
- When you define the ABCs of a service in code you can of course then change the default settings
- Simply change the properties of the object
- Syntax/example:
void ConfigureBinding()
{
BasicHttpBinding binding = new BasicHttpBindting();
binding.OpenTimeout = TimeSpan.FromSeconds(30);
// other code…
} - You can also use a default binding in your
app.config
file but then adjust as necessary - Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="MySvcLib.MySvc">
<host>
<baseAddresses>
<add baseAddress="http://blahblah…"/>
<add baseAddress="net.tcp//blahblah…"/>
</baseAddresses>
</host>
</service>
</services>
<!-- Modify default behaviors -->
<bindings>
<basicHttpBinding>
<binding openTimeout="00:00:30" />
</basicHttpBinding>
<netTcpBinding>
<binding openTimeout="00:00:15" />
closing tags… - Note: for .NET 3.5 & earlier the mark-up to accomplish this worked a little differently. See Troelsen: 5th Ed., pp. 1053-4 for a discussion & an example.
- The WCF 4.0 default MEX behavior configuration
- Any client-side proxy generation tool must be able (at run time) to get info on the target service in order to work
- In WCF MEX is the tool which makes the appropriate information about the service available
- Note: MEX really only needs to be enabled during development (i.e., of the client(s))
- Because MEX configuration is quite standard .NET 4.0 allows some short-cuts
- In fact, do not need to do as done above, i.e.…
- Add a MEX endpoint
- Define a named MEX service behavior
- Then connect named binding to your service
- Rather, simply add a
<serviceMetadata httpGetEnabled="true" />
element (inside<behaviors>
, etc. elements - Difference vs. earlier example
- You do not include the
name
attribute inside the<serviceMetadata>
element - This, in turn, means you drop
behaviorConfiguration
attribute from your<service>
element
- You do not include the
- In fact, do not need to do as done above, i.e.…
- Refreshing the client proxy & selecting the binding
- Must make sure
- Host is running
- You have started VS in administrator mode
- In VS with client solution
- In right-click on the service reference and slect
- Look insider your
app.config
file forname
attributes of your bindings - In your code pass the desired name as a string into the constructor of your service
- Must make sure
- General
- Using the WCF service library project template
- Building a simple service (per Troelsen: 5th Ed., pp. 1057-8)
- Rename the
IService1.cs
&Service1.cs
files - Regarding the interface code file
- Add an appropriate
Namespace
property (& value) to theServiceContract
attribute - Delete the:
CompositeType
classGetDataUsingDataContract()
method definitionGetData
method definition- Note: your interface will now effectively be empty
- Add your desired methods
- Add an appropriate
- Regarding the service code file
- Delete all contained code - i.e., the 2 methods:
GetData()
GetDataUsingDataContract
- Add in your desired methods (i.e., implement the interface)
- Delete all contained code - i.e., the 2 methods:
- Open your
app.config
file- Verify that all instances of
IService1
&Service1
have been appropriately renamed - 2 default settings of note:
- MEX is enabled
- Service endpoint set to use
wsHttpBinding
protocol
- Verify that all instances of
- Rename the
- Testing the WCF service with
WcfTestClient.exe
- Big advantage of creating your service using the WCF Service Library poject template is that you library is automatically 'hooked into' the WCF Test Client application (i.e.,
WcfTestClient.exe
- Simply invoke or to launch
- Double-click on method of choice to test
- Note: the utility does not require that services be created via the WCF Service Library project
- Your service must have an MEX endpoint
- Pass the service into the utility as a command line argument
- Big advantage of creating your service using the WCF Service Library poject template is that you library is automatically 'hooked into' the WCF Test Client application (i.e.,
- Altering configuration files using
SvcConfigEditor.exe
- In
App.config
file to launchSvcConfigEditor.exe
right-click on - Note: can also do this from a client app referencing a WCF service
- Advantages of using
SvcConfigEditor.exe
- Assures that your config file conforms to the schema
- Assures that your config file is typo-free
- Allows you to see all allowable values for any given attribute
- Can use to create new
*.config
files- Launch from VS command prompt
- This means that you can use
SvcConfigEditor.exe
to create your*.config
files on start-from-scratch WCF libraries
- Troelsen (: 5th Ed., p. 1060) notes that
SvcConfigEditor.exe
has a very good F1/Help system
- In
- Building a simple service (per Troelsen: 5th Ed., pp. 1057-8)
- Hosting a WCF service within a Windows service
- General
- Defining an app to host your service is generally not a good idea - accidentally closing that app will disconnect any users connected to the service
- Specifying the ABCs in code
- Enabling MEX
- Creating a Windows service installer
- Installing the Windows service
- General
- Invoking a service asynchonously from a client
- Designing WCF data contracts
- General
- Using the web-centric WCF Service project template
- Implementing the service contract
- The role of the
*.svc
file - Examining the
Web.config
file - Testing the service
- Simplifying configuration settings with WCF 4.0
- Distributed computing APIs
- Windows Workflow Foundation 4.0
- Defining a business process
- General
- "Simply put, a business process is a conceptual grouping of tasks that logically work as a collective whole." (Troelsen: 5th Ed., p. 1078)
- Historically, code to handle a business process & charts & diagrams describing a process were independent
- Meant a programmer had to use something like Visio to explain a process to business users
- Changing a diagram didn't automatically change the underlying code - & visa versa
- Role of WF 4.0
- In a nutshell, WF "allows programmers to declaratively design business processes using a prefabricated set of activities " (Troelsen: 5th Ed., p. 1078 - italics in the original)
- Can thus use WF designers with VS to sketch out a business process at design time
- (Implementation code can be added later)
- All means that a single WF entity includes both
- Visual description of the business process
- The code implementing that process
- Advantages
- Code & diagrams are never out of sync
- Code "illustrates itself"
- General
- Building a simple workflow
- General
- A model of your business process is built right into your underlying, implementation code
- WF uses XAML (though the XAML is not exactly like WPF XAML)
- Note: you can create console-based WF apps
- Creating a new WF app presents one with with a design window
- If using a pre-fab activity just drag-and-drop (where it says "Drop activity here"!)
- Notes re:
WriteLine
primitive- Displays text to a
TextWriter
- When working with a WF console app the console serves as the
TextWriter
- Displays text to a
- The underlying XAML
- To explore close the worklfow designer, then
- Root element is
<Activity>
- Will see scads of namespace definitions inside
- Most of the namespace definitions contain
clr-namespace
tokens clr-namespace
tokens are how XAML references external assemblies- At this point all of those assemblies are .NET assemblies
- Most of the namespace definitions contain
- Generating the XAML
- Typically you don't directly modify the XAML
- Automatically generated by the IDE & design window
- XAML & runtime engine(s)
- All XAML elements map directly to a .NET type
- For example,
<Activity>
is a declarative way to referenceSystem.Activitties.Activity
type - Element attributes equate to type properties
- At runtime the XAML is fed into a runtime engine to set the state of the appropriate .NET object(s)
- Benefits of utilizing XAML to define structure of a workflow
- Could host the WF design window in a custom GUI app!
- This would allow non-developers to set a workflow
- Can then save the
*.xaml
file & import it into a .NET project
- Can also import
*.xaml
files on the fly- Note this means that workflow logic is separate from implememtation
- Changing workflow itself then means only restarting your app
- Could host the WF design window in a custom GUI app!
- General
- The WF 4.0 runtime
- General
- The WF runtime is an entity unto itself
- Responsibilities
- Load & unload defined workflows (i.e., the
*.xaml
- Execute the workflow
- Perform other required manipulations of the workflow
- Load & unload defined workflows (i.e., the
- WF runtime & domains
- The WF runtime can be hosted in any .NET app domain
- This means WF can be embedded in any kind of .NET app
- Console apps
- WPF apps
- WCF services
- Note: an app doman can only host 1 instance of the runtime
- Workflow Activity Library projects
- Sometimes you may have to define a business process that will be consumed by a variety of systems
- In this type of project you bundle your workflow into a
*.dll
- Hosting a workflow using
WorkflowInvoker
- General
- Several ways for host app to interact with the WF runtime
- Easiest is via
WorkflowInvoker
class - In a WF console app the invocation is added automatically
- Syntax/example:
class Program
{
static void Main(string[] args)
{
Activity workflow1 = new Workflow1();
WorkflowInvoker.Invoke(workflow1);
}
} WorkflowInvoker.Invoke()
is appropriate for simple situations- Method starts your workflow in a synchronous blocking manner
- Calling thread is blocked until workflow finishes completely (or is terminated)
- This means workflow must finish before
Main()
can finish
- Appropriate when you do not need to monitor the workflow
- Method starts your workflow in a synchronous blocking manner
- Passing arguments to your workflow using
WorkflowInvoker
- You typically want to pass in some kind of initial parameters to your workflow
- Normally in .NET you create some kind of custom constructor to handle this with your custom classes
- Worklfows, however, are different
- Always created with its default constructor
- Further, defined in XAML, not in procedural code
- However,
WorkflowInvoker.Invoke()
is overloaded several times - One version allows you to specify start-up parameters
- You pass in a
Dictionary<string, object>
parameter - You populate that
Dictionary<string, object>
parameter with pairs of values identically named & typed to those within the workflow itself
- You pass in a
- Defining arguments using the workflow designer
- Typically do this in the designer window
-
- Rename the argument from
argument1
- Specify
- Direction
- Argument type
- Default value (optional)
- Rename the argument from
- Using
WriteLine
activity- If you created a
string
argument calledMyMsg
you can then type that argument into theWriteLine
Text box MyMsg
will show up in intellisense as you do so!
- If you created a
- Calling on your custom arguments within code
- Note: will have to import
System.Collections.Generic
namespace in order to useDictionary<string, object>
- Syntax/example (if using a wf with a single
WriteLine
activity):
class Program
{
static void Main(string[] args)
{
// Get data from the user, pass it to the wf…
Console.Write("Enter data to pass to the wf: ");
string wfData = Console.ReadLine();
// Package up the data as a dictionary…
Dictionary<string, object> wfArgs =
new Dictionary<string, object>();
wfArgs.Add("MyMsg", wfData);
// Pass to the wf…
WorkflowInvoker.Invoke(new Workflow1(), wfArgs);
Console.ReadLine();
}
}
- Note: will have to import
- Final notes on
WorkflowInvoker
- Also has
BeginInvoke()
&EndInvoke()
methods - Use asynchronous delegate pattern to allow you to fire up a workflow on a secondary thread
- Also has
- General
- Hosting a workflow using
WorkflowApplication
- Use this for longer-running workflows
- Opens door for you to use features like
- Using WF persistence services to save or load a long-running workflow
- Event notification
- Workflow bookmarks
- Others
- Use the type's
Run()
method to start a workflow- A new thread gets pulled from the CLR thread pool
- This means you must add plumbing to ensure that your main thread waits for this new, secondary thread
- You typically do this by using an
AutoResetEvent
object (fromSystem.Threading
namespace)
- Syntax/example (showing only modification from above
WorflowInvoker.Invoke()
example):
static void Main(string[] args)
{
// Other code as above…
// Used to inform primary thread to wait…
AutoResetEvent waitHandle = new AutoResetEvent(false);
// Pass to the wf (replaces WorkflowInvoker.Invoke())…
WorkflowApplication app =
new WorkflowApplication(new Workflow1(), wfArgs);
// Hook up an event with this app. When done notify other
// thread & print a message…
app.Completed = (completedArgs) => {
waitHandle.Set();
Console.WriteLine("The wf is done!");
};
app.Run(); // …start the wf
// Wait until notified the wf is done…
waitHandle.WaitOne();
Console.ReadLine();
} - Note: Use of
Completed
property is at least an indirect example of how you can hook into events WorkflowServiceHost
- Another mechanism for staring & monitoring a Workflow instance
- Class supports
- Messaging acivities
- Multi-instancing
- Configuration
- Class used automatically if you build a workflow-enabled service under IIS
- General
- The Workflow 4.0 activities
- General
- Acivities represented by icons found in the Designer toolbox
- Each such icon maps to a type
- All types in
System.Acivities.dll
- Most types in
System.Activities
namespace
- All types in
- Control flow activities
DoWhile
ForEach<T>
- iterate throughForEach<T>.Values
collectionIf
Parallel
- executes all child activities simultaneouly & asynchronouslyParallelForEach<T>
Pick
- for event-based flow controlPickBranch
- Always a child element of a
Pick
element - Identifies a possible branch of execution
- Always a child element of a
Sequence
Switch<T>
While
- Notes:
- Some of these activities execute activities in parallel
- This will entail - at least behind the scenes - utilizing the Task Parallel Library
- Flowchart activities
- General
- These activities are important because a flowchart activity is typically the first item placed on your wf designer
- Flow chart workflow is a new concept as of WF 4.0
Flowchart
- Used to model workflows Note:
xx
xx
xx
< xx >
- Typically the first item placed on your wf designer
- Used to model workflows Note:
FlowDecision
- allows for 2 possible outcomesFlowSwitch<T>
- a switch construct
- General
- Messaging activities
- General
- Messaging activities allow wf to work with
- External XML service
- WCF service
- Because of close integration with WCF services these capabilities are placed in a dedicated assembly,
System.ServiceModel.Activities.dll
- Namspace is same as assembly name
- Messaging activities allow wf to work with
- Activities
CorrelationScope
- for managing child message activitiesInitializeCorrelation
- Fires up correlation
- Does so without sending or receiving a message
Send, Receive
- these are the 2 most common messaging activitiesSendAndReceiveReply
- as advertisedTransactedReceiveScope
- "enables you to flow a transaction into a workflow or dispatcher-created server side transactions" (Troelsen: 5th Ed., p. 1090)
- General
- Runtime & Primitives activities
- General
Persist
&TerminateWorkflow
provide the ability to make calls to the wf engine- The categories of activities also allow you to:
- Push text to an output stream
- Invoke a method on a .NET object
- Activities
Persist
- Relies on WF persistence service
- Causes wf to save its state in a db
TerminateWorkflow
- Raises the
WorkflowApplication.Completed
event - Reports any error information
- Notes:
- A terminated workflow cannot be resumed
- Use this to handle reaching a point of no return
- Raises the
Assign
- Use this to set the values of an activity's properties
- Works with the values defined within the wf designer
Delay
InvokeMethod
- Probably the most useful of the runtime & primitives activities
- Allows you to call .NET methods from XML
- Can also capture & hold onto any values returned by an invoked method
WriteLine
- Default recipient is the console, a.k.a., the standard output stream
- However, can work with any
TextWriter
-derived type (e.g., aFileStream
)
- General
- Transaction activities
- General
- As with dbs, with wfs you sometimes need to group acivities so that if one fails the whole group is regarded to have failed
- This applies with wfs even when you are not working against a db
- Activities
CancellationScope
- sets how cancelation logic works within main path of executionCompensableActivity
- relates to compensating child activitiesTransactionScope
- sets transaction boundary
- General
- Collection & error handling activities
- General
- Collection activities
- Extremely useful when you need to manipulate business objects on the fly in XAML
- Business object could be
- Purchase orders
- Medical info objects
- Order tracking objects
- Error activities - for implementing try/catch/throw logic within a wf
- Collection activities
- Activities
AddToCollection<T>
ClearCollection<T>
ExistsInCollection<T>
RemoveFromCollection<T>
Rethrow
Throw
TryCatch
- General
- General
- Building a flowchart workflow
- General
- Two activities typically form the root of a wf
FlowChart
Sequence
- Sub-activities
- Obviously important to start a wf with activities that support sub-activities
- Only a minority of activities have that capability
- Again, this is where
FlowChart
&Sequence
typically come into play
- Two activities typically form the root of a wf
- Connecting activities in a flowchart
- Start icon
- Represents the activity triggered using
WorkflowInvoker
orWorkflowApplication
class - Generally position this in upper left corner of design window
- Represents the activity triggered using
- Adding activities
- Typically connect them via
FlowDecision
- As you add activities you connect one to the next by hovering your mouse over one of the 4 docking panels on the sides of activity #1 & then dragging to one of the docking panels on activity #2
- Typically connect them via
- Start icon
- The
InvokeMethod
activity- In Troelsen's example (Troelsen: 5th Ed., pp. 1094‑5) he shows how to capture a "Y/N" answer captured from a console prompt
- After dragging an
InvokeMethod
activity icon onto designer, & then attaching it to the appropriateWriteLine
activity (asking the Y/N question)- The method which one wants to invoke (in this example) is
Console.Readline()
- In the
- Drill down to
mscorlib.dll.System.Console
- You can also type "Console" in the search box
- Drill down to
- In the
ReadLIne
(sans the ending parentheses) box type
- The method which one wants to invoke (in this example) is
- Defining workflow-wide variables
- Use the button at the bottom of the designer window
Arguments
vs.Variables
Arguments
are what one uses to capture values passed in by the host- "…variables are simply points of data in the workflow which will be used to influence its runtime behavior." (Troelsen: 5th Ed., p. 1095)
- In our Yes/No example we thus need to define a
variable
to capture the user's answer Variable
scope- In designer window
- Will only see multiple options if you have a workflow with multiple parent containers
- Otherwise will only see the name of the parent wf
- To define an array
- In
- From dialog box, well, select the type
- Making use of a variable in our example
- Click on activity
- Begin typing in the name of your variable
- The
FlowDecision
activity- Reminder…
- Again, this is for handling
True/False
conditions - Use
FlowSwitch<T>
activity for multiple branching decisions
- Again, this is for handling
- , then enter a C# statement
- That statement must evaluate to a boolean (obv…)
- Example:
- You pass in a string value called
YesOrNo
- You want to set your condition to:
YesOrNo.Trim().Substring(0,1).ToUpper() == "Y";
- You pass in a string value called
- Reminder…
- The
TerminateWorkflow
activity- You actually don't need this activity - a wf will end when a branch it's on reaches a dead end
- However, the activity allows you to throw an exception to the wf host
- Then, in dialog box type in something like
new SystemException("User said No!");
- A conscientious developer also provides text in the box
- The
ForEach<T>
activity- As with some other activities, the
ForEach<T>
activity has its own designer window- These activities will sport a
Double-click to view
message - As you drill into these sub-designer windows you can navigate back up via the bread-crumbs at the top of main designer window
- These activities will sport a
- The
ForEach<T>
sub-designer dialog- The
item
- leave as is box should show - In box type in name of your array (intellisense will help)
- In (as directed) you will drag & drop an activity
- The
- As with some other activities, the
- Completing the applcation
- Wrap the code invoking the wf inside a
Try/Catch
blocks - This way you can handle any exceptions thrown - esp. those thrown by a
TerminateWorkflow
activity- Recall that
TerminateWorkflow
has aReson
property - As such, you can get the proivided info via an indexer on the
Data
property of your caught exception
- Recall that
- Syntax/example: from Troelsen: 5th Ed., p. 1101)
static void Main(string[] args)
{
try
{
Dictionary<string, object> wfArgs = new Dictionary<string, object>();
wfArgs.Add("UserName", "Bill");
WorkflowInvoker.Invoke(new Workflow1(), wfArgs);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.Data["Reason"]);
Console.ReadLine();
}
}
- Wrap the code invoking the wf inside a
- General
- Isolating workflows into dedicated libraries
- General
- Almost always done in production
- Choose when creating your new project
- Defining the initial project
- Renaming your
*.xaml
file- Good practice
- After doing so, however…
- Close your designer window
- Right-click on
*.xaml
file & - Check
x:Class
attribute of root<Activity>
element - Typically, this will still be set to
Activity1
- if so rename it
- Client programs use the assigned value of the
x:Class
attribute to refer to the wf - Wf libraries typically start with a
Sequence
rather than aFlowChart
activity
- Renaming your
- Importing assemblies & namespaces
- Defining the workflow arguments
- Defining workflow variables
- Working with the
Assign
activity - Working with the
If
andSwitch
activities - Building a custome code activity
- General
- Consuming the workflow library
- General
- Retrieving the workflow
Output
argument
- Defining a business process
- Multithreaded & Parallel Programming
- WPF
- WPF & XAML
- General
- Motivation behind WPF
- Unifies diverse APIs
- Separates UI markup from programming code
- Note: Any application can use XAML to describe a tree of .NET objects; XAML is not inherently restricted to UI stuff
- Custom WPF library is needed only to change the behavior of a control
- Look and feel of control is governed via markup
- Rendering model
- Windows Forms & VB6 use a C-based API (GDI) for graphical rendering
- GDI is an old, long-standing part of Windows OS
- Higher performance graphics historically required DirectX
- WPF works against DirectX, not against GDI
- Types of WPF applications
- Desktop apps
- The
*.exe
files for these apps are handled just like any .NET desktop app - At a minimum these apps make use of following types:
Window
Application
- The
- Navigation-based apps
- This is where you make a desktop app look and feel like a web app
- Create forward and back buttons to navigate between various UI displays called pages
- The app itself:
- Maintains a list of each page
- Provides infrastructure to navigate among them
- Passes data across pages
- Maintains a history list
- Typically makes use of the following types:
Application
Page
NavigationWindow
Frame
- XBAP (i.e., XAML Browser application)
- General
- XBAP is hosted within a web browser
- XBAP is essentially a collection of
Page
objects - App sits on a URL which user navigates to
- App is then transparently downloaded and installed on user's machine
- XBAP is not displaying ASP.NET pages
- Advantages
- XBAP inherits browser's navigation system
- XBAP can have much more functionality than a web page
- XBAP need only be deployed/updated at its home URL
- Requirements/Limitations
- Can only run inside IE & FireFox browsers
- Ergo, can only run on Windows machines
- User's machine must have local installation of .NET framework
- In practice, would only deploy an XBAP as an internal corporate app
- General
- Silverlight
- A cross-browser, cross-platform WPF-based technology
- Competitor to Adobe Flash (but you program with C# & XAML)
- Subset of WPF functionality
- In reality, Silverlight == unique distribution of .NET platform
- Contains a "mini" CLR
- Contains a "mini" version of base class libraries
- Not restricted to Windows; Mac OS X version available
- There is also an open-source, Linux-based version of Silverlight (see The Mono project)
- Desktop apps
- Motivation behind WPF
- Core WPF assemblies, namespaces, & classes
- Core WPF assemblies
PresentationCore.dll
- Contains numerous types defining foundation of WPF GUI layer
- Specifically, supports
- Ink API
- Several animation primitives
- Graphical rendering types
PresentationFramework.dll
- contains:- Majority of WPF controls
Application
classWindow
class- Support for interactive 2D stuff
- Functionality to read & write XAML docs at runtime
System.Xaml.dll
- Allows you to program against XAML docs at runtime
- Typically used only in advanced apps
WindowsBase.dll
- Contains types having to do with the infrastructure of the WPF API
- Examples:
- Threading types
- Security types
- Miscellaneous type converters
- Support for dependency properties and routed events
- Core WPF namespaces
System.Windows
- Root namespace of WPF
- Contains the
Application
&Window
classes
System.Windows.Controls
- includes:- The predictable controls
- Types to build menu systems & tool tips
- Various layout managers
System.Windows.Data
- for working with databinding engines and templatesSystem.Windows.Documents
- Contains types having to do with the documents API
- That API employs XML Paper Specification (XPS) protocol
System.Windows.Ink
System.Windows.Markup
- allows XAML markup (& its binary format equivalent, BAML) to be parsed & processed programmaticallySystem.Windows.Media
- Itself contains several media-specific namespaces
- Use this namespace to handle:
- Animations
- 3D rendering
- Text rendering
- Other multimedia primitives
System.Windows.Navigation
System.Windows.Shapes
- for working with interactive 2D graphics
- Assemblies not related to WPF:
System.Windows.Forms.*
System.Drawing.*
System.Windows.Application
class- General
System.Windows.Application
class == global instance of the WPF application that's runningRun()
method invoked to start the app- Events for interacting with application's lifetime:
Startup
Exit
- Key properties:
Current
(static)- Gives you access to the running
Application
object from anywhere in your code - Ergo, gives you access to app-wide variables, methods, etc.
- Gives you access to the running
MainWindow
- use to get or set main windowProperties
- Use to establish (or gain access to) app-wide data, objects, etc.
- Available through all aspects of a WPF app
- Allows you to define name/value pairs via a type indexer
- Indexer operates on type
System.Object
so you can set properties to anything
StartupUri
- gets or sets specific page or window to open when app startsWindows
- returns aWindowsCollection
type
- Constructing an
Application
class- In WPF projects create a class which inherits from
Application
class - Define the overall program's entry point as such:
class MyApp : Application
{
[STAThread]
static void Main(string[] args)
{
// Create the application obj…
MyApp app = new MyApp();
// Register start-up & exit events…
app.Startup += (s, e) => { // Start up the app }
app.Exit += (s, e) => { // Exit the app }
}
} [STAThread]
attribute- This ensures that any legacy COM objects used by the app are thread-safe
- Omitting the attribute will cause a runtime error
- In WPF projects create a class which inherits from
- Enumerating the
Application.Windows
collection- Every
window
new-ed into existence is a member of the collection - Typically access collection via
Application.Current.Windows
- Every
- General
System.Windows.Window
inheritance chain- Note: All dialog boxes displayed by main window are members of this class
System.Windows.Controls.ContentControl
class- Direct parent of
Window
class - Base class holds a single piece of content
- Refers to "data placed within the interior of the control's surface area" (Troelsen: 5th Ed., p. 1131)
- With a button, for example, can place content other than text on surface
- When content is simple text:
<Button Height="80" Width="100" Content="OK" />
- When content, however, is complex data:
- Implicit definition (example):
<Button Height="80" Width="100" />
<StackPanel>
<Elipse Fill="Red" Width="25" Height="25"/>
<Label Content="OK!" />
</StackPanel>
</Button> - Can also use property-element syntax (i.e., explicit) definition (example):
<Button Height="80" Width="100" />
<Button.Content>
<StackPanel>
<Elipse Fill="Red" Width="25" Height="25" />
<Label Content="OK!" />
</StackPanel>
</Button.Content>
</Button>
- Implicit definition (example):
- Note: not all controls are children of
ContentControl
- This means that content for certain controls cannot be set in the same ways just shown
- Further, some controls add wrinkles to how content is set
- Direct parent of
System.Windows.Controls.Control
class- Direct parent of
ContentControl
class - All WPF controls share the
Control
base class as common parent - Defines properties relating to a control's basic UI functionality - i.e., size, tab order, colors, etc.
- Class also supports templating services
- Content can be set only once
- With a
Window
object, where you typically want multiple controls, use a layout manager (e.g.,DockPanel
) as your 'single' control
- Direct parent of
System.Windows.FrameworkElement
class- Direct parent of
Control
class - Used for
- Storyboarding (used in animations)
- Databinding
- Obtaining resources defined by derived type
- Direct parent of
System.Windows.UIElement
class- Direct parent of
FrameworkElement
class - Provides greatest amount of flexibility
- Purpose of class: provide derived types with events re:
- Receiving focus
- Processing input requests
- Visibility
- Hit-testing logic
- Direct parent of
System.Windows.Media.Visual
class- Direct parent of
UIElement
class - Provides core rendering support
- Is essentially the shim between .NET and DirectX subsystem
- Direct parent of
System.Windows.DependencyObject
class- Direct parent of
Visual
class - Provides dependency properties - this is where property value is computed based on values of other properties
- Provides attached properties - this is a form of dependency properties useful in:
- WPF's data-binding model
- Laying out UI elements within various WPF panels
- Two essential methods:
GetValue()
andSetValue()
- Can also register who can use the properties in question
- Direct parent of
System.Windows.Threading.DispatcherObject
class (used in threading)- Direct parent of
Dependency
class - Used in threading
- Inherits directly from
System.Object
- Direct parent of
- Core WPF assemblies
- Building WPF assemblies
- Coding samples without using XAML (see Troelsen: 5th Ed.)
- Basic syntax:
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyWpf
{
// Define single class type to represent app itself & main window
class Program : Application
{
[STAThread]
static void Main(string[ ] args)
{
// Handle Startup & Exit events, then run the app…
Program app = new Program();
app.Startup += AppStartUp;
app.Exit += AppExit;
app.Run(); // Fires StartUp event
}
static void AppExit(object sender, ExitEventArgs e)
{ // Perhaps a msg that app has exited… }
static void AppStartUp(object sender, StartUpEvents e)
{
// Create a Window object & set some properties…
Window mainWindow = new Window();
mainWindow.Title = "Some Title"; // etc.…
mainWindow.Show(); // Umm… show the window
}
}
} - Creating a strongly-typed window
- Instead of above approach, you ideally create a separate class for your
Window
object - Syntax/example:
class MyMainWindow : Window
{
public MyMainWindow(string winTitle, int height, int width)
{
this.Title = winTitle;
// etc.…
}
} - Note: Regardless of how you create a
Window
object it is always added automatically to theApplication.Windows
collection/property
- Instead of above approach, you ideally create a separate class for your
- Creating a simple UI
- Steps:
- Define a member variable of the control's type
- Configure look & feel of control (this configuration will occur when the parent
Window
is instantiated) - Attach the control to the
Window
by either:- Inherited
content
property ofWindow
-derived type - Pass the control in as a parameter to the inherited
AddChild()
method
- Inherited
- Syntax/example:
class MyMainWindow : Window
{
// Define UI element…
private Button btnExitApp = new Button();
public MainWindow(string winTitle, int height, int width)
{
// Configure button & set child control…
btnExitApp.Click += new RoutedEventHandler(btnExitApp_Clicked);
btnExitApp.Content = "Exit App";
// Set button height, width, etc.…
// Set content of window to button…
this.Content = btnExitApp;
// Configure the window…
this.Title = winTitle; // Set height, width, other ppts…
this.Show();
}
private void btnExitApp_Clicked(object sender, RoutedEventArg e)
{ this.Close(); }
} - Reminder: Content (i.e., of a
Window
) can only be set once! - Note: As above example indicates, a
RoutedEventsHandler
delegate (to be discussed later) takes 2 parameters:- 1st:
object
- 2nd:
RoutedEventsArgs
- 1st:
- Steps:
- Interacting with application-level data
- Can create custom
Application
data withProperties
property Properties
is an indexer which takesSystem.Object
(i.e., anything) as a parameter- Example:
Application.Current.Properties["ProductionMode"] = true;
- Remember that you can access such data from anywhere in app via, say:
if((bool)Application.Current.Properties["ProductionMode"])
// Some code…
- Can create custom
- The two events fired when closing a
Window
objectClosing
- Works in conjunction with
CancelEventHandler
delegate - Second parameter of methods targeted by delegate are of type
System.ComponentModel.CancelEventArgs
CancelEventArgs
takes a booleanCancel
property
- Works in conjunction with
Closed
- See Troelsen: 5th Ed., pp. 1141-2, for code example
- Intercepting mouse events (pp. 1142-3)
- Captured via
UIElement
base class - Mouse events work with
System.Windows.Input.MouseEventHandler
delegate - Delegate takes
System.Windows.Input.MouseEventArgs
as second parameter GetPosition()
method gives(x, y)
position of mouse- Note also the
XButton1
andXButton2
properties - these are the "extra" buttons on the sides of a mouse - One useful event:
MouseMove
- Captured via
- Intercepting keyboard events (pp. 1143-4)
- Like mouse events, also captured via
UIElement
base class - Keyboard events work with
System.Windows.Input.KeyEventHandler
delegate - Delegate takes
System.Windows.Input.KeyEventArgs
as second parameter
- Like mouse events, also captured via
- Basic syntax:
- Using XAML
- General
- Though rare, XAML can be used with non-WPF .NET applications
- Can be used to describe a tree of .NET types
- Types would all have to be nonabstract
- Types would each have to support a default constructor
- Attributes within the scope of an opening element used to set:
- Properties
- Events
- Example:
<Button Name="btnClickMe"
Height="40"
Width="100"
Content="Click Me"
Click="btnClickMe_Clicked" />
- "XAML is simply a declarative way to define the state of an object" (Troelsen: 5th Ed., p. 1145)
- Files are saved with
*.xaml
extensions
- Though rare, XAML can be used with non-WPF .NET applications
- XML syntax
- Default namespace for WPF is:
http://schemas.microsoft.com/winfx/2006/xaml/presentation
- Also generally use (with a
x:
token):
http://schemas.microsoft.com/winfx/2006/xaml
- Within opening element of
Window
orApplication
element(s) set class definition- Use
x:Class
attribute - Use
Namespace.Classname
syntax when setting the property
- Use
- Default namespace for WPF is:
- To put event-handling code within XAML (rarely done)
- Use
x:Code
element - Then wrap C# (or VB) code within
<![CDATA[
and]]>
tags
- Use
- Defining MainWindow in XAML (syntax/example):
<Window x:Class="MyNmsp.MainWin"
xmlns="http://schemas.microsoft.com/winfix/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My Title"
<!-- other properties -->
Height="200">
<!-- Button content -->
<Button x:Name="btnExitApp" Content="Close Window"
Click = "btnExitApp_Clicked" />
</Window> - Defining the
Application
object in XAML- More problematic than marking up MainWindow
- In C# code you of course (must) define a
Main()
method as an entry point for your app - If you are, in fact, defining the
Application
in XAML mark-up as substitute forMain()
method include the following within the openingApplication
element:- You must supply a a substitute for the
Main()
method - You do so by including the following within the opening
<Application>
element:StartupUri
attribute- Set value of that attribute to name of main
Window
file (e.g.,MainWindow.xaml
)
- You must supply a a substitute for the
- Syntax/example:
<Application x:Class="XamlApp.MyApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StatrtupUri="MainWindow.xaml">
</Application>
- Processing XAML files using
msbuild.exe
- General
- C# compiler incapable of handling raw XAML markup
- Must use
msbuild.exe
(command-line utility)- Must provide correct
*.targets
files - Transforms XAML markup into C# code
- Utilizes an XML-based build script
- These scripts contain exactly same sort of information that are in a
*.csproj
file generated by VS
- Must provide correct
*.csproj
files- These are created by VS when compiling a C# project
- XML-based file which includes external references, version numbers, GUIDs, etc.
- With XAML must create
*.csproj
file "by hand"
*.csproj
file syntax- Root element:
<Project>
- Opening
<Project>
element must includeDefaultTargets="Build"
attribute & property declarationxmlns="http://schemas.microsoft.com/developer/msbuild/2003"
namespace declaration
- In-scope
PropertyGroup
element, with at least 3 child elements:RootNamespace
AssemblyName
(do not include*.xaml
extension when specifying contentOutputType
(content:winexe
)
- In-scope
ItemGroup
element- The first
ItemGroup
element should itself contain one (empty)Reference
element for each external assembly - Syntax:
<Reference Include="System" />
- The first
- A second (in-scope)
ItemGroup
element which, in turn, contains 2 other (empty) elements:- First child element specifies the
*.xaml
file containing the application object - Syntax:
<ApplicationDefinition Include="SomeApp.xaml" />
- Remaining child elements specify
Window
andPage
elements in the project - Syntax:
<Page Include="SomeWindowFileName.xaml" />
- First child element specifies the
- Finally, two in-scope
Import
elements, each of which contain aProject
attribute- Used to specify 2
*.targets
files - One file contains the "build settings [needed] to transform the XAML definitions into equivalent C# code files" (Troelsen: 5th Ed., p. 1149)
- Attribute/value syntax:
Project ="$(MSBuildBinPath)\Microsoft.Csharp.targets"
- Second file "contains data to interact with the C# compiler itself" (Troelsen: 5th Ed., p. 1149)
- Attribute/value syntax:
Project ="$(MSBuildBinPath)\Microsoft.WinFX.targets"
- Used to specify 2
- Sample markup:
<Project DefaultTargets="Build"
xmlns="http:/schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<RootNamespace>MyXamlApp</RootNamespace>
<AssemblyName>MyXamlApp</AssemblyName>
<OutputType>winexe</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="MyApp.xaml" />
<Page Include="MainWindow.xaml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildBinPath)\Microsoft.WinFX.targets" />
</Project>
- Root element:
- Running
msbuild.exe
(it's run from the VS Command Prompt)- Syntax:
msbuild SomeFileName.csproj
- After running
msbuild
working directory will have a\bin
and\obj
subdirectories - A .NET assembly is created and placed in
\bin\Debug
folder
- Syntax:
- General
- General
- How XAML markup is transformed into a .NET assembly
- Mapping the
Window
XAML data to C# code- Process controlled by the 2
*.targets
files msbuild.exe
creates 2*.g.cs
files- These are regular C# files (i.e., can be opened in Notepad or VS)
- The
.g
signifies that the files were autoGenerated - Files are located in the
\obj\Debug
subdirectory of app folder
- "Additions" that
msbuild.exe
makes when creating the*.g.cs
file for theWindow
object- A partial class extending the
System.Windows.Window
base class - An inheritance statement for
System.Windows.Markup.IComponentConnector
interface (to handle thebutton
) - The
Connect()
method required by theIComponentConnector
interface - A class-scoped
boolean
, named_contentLoaded
(serves to ensure the content of the window is assigned only once) - An
InitializeComponent()
method- This is not in place to handle look-and-feel properties of the
Window
object - Rather, specifies "the location of an embedded assembly resource that is named identical [sic] to the original
*.xaml
file" (Troelsen: 5th Ed., p. 1151) - The embedded resource is of type
System.Uri
- This is not in place to handle look-and-feel properties of the
- A partial class extending the
- Process controlled by the 2
- The role of BAML
msbuild.exe
generates a file with a*.baml
extension- This file is located in the
\obj\Debug
folder BAML
stands for Binary Application Markup LanguageBAML
file is a binary representation of the original*.xaml
file- The generated
*.baml
file is embedded as a resource in the compiled assembly - Note: because of existence of
*.baml
file you never have to distribute your XAML mark-up itself when deploying a WPF application
- Mapping the
Application
XAML data to C# code- This is the 2nd
*.g.cs
file generated bymsbuild.exe
- "Additions" that
msbuild.exe
makes when creating this*.g.cs
file for theApplication
object- A partial class extending the
System.Windows.Window
base class Main()
method- Assigned
[System.STAThreadAttribute]
- Assigned
[System.Diagnostics.DebuggerNonUserCodeAttribute]
- In turn calls an
InitializeComponent()
method
- Assigned
- An
InitializeComponent()
method- Also assigned
[System.Diagnostics.DebuggerNonUserCodeAttribute]
- Implements
this.Exit
event handler (delegate typeSystem.Windows.ExitEventHandler
) - Defines
this.StartupUri
(of typeSystem.Uri
)
- Also assigned
- A partial class extending the
- This is the 2nd
- Summary of XAML-to-Assembly process (see Figure 27-12, Troelsen: 5th Ed., p. 1154)
- Mapping the
- Coding samples without using XAML (see Troelsen: 5th Ed.)
- WPF XAML syntax
- Kaxaml
- Cannot include:
- Any markup entailing code compilation
- The
x:Class
attribute (for specifying a code file) - Any event handlers
- Any XAML keywords that entail/imply code compilation (e.g.,
FieldModifier
orClassModifier
)
- Can specify
x:Name
, though
- Cannot include:
- XAML XML namespaces & XAML "keywords"
schemas.microsoft.com/winfx/2006/xaml/presentation
namespace- Maps to multiple WPF.NET namespaces
System.Windows
System.Windows.Controls
System.Windows.Data
System.Windows.Media
System.Windows.Navigation
- Others
- These namespaces are actually found across several WPF assemblies
WindowsBase.dll
PresentationCore.dll
PresentationFramework.dll
- The one(xml namespace)-to-many(WPF namespaces) mapping
- Achieved via the
[XmlnsDefinition]
assembly-level attribute applied to the multiple assemblies - Mappings are visible in
reflector.exe
- Achieved via the
- Maps to multiple WPF.NET namespaces
schemas.microsoft.com/winfx/2006/xaml
namespace- References the
System.Windows.Markup
namespace - Used to include XAML-specific keywords
x:Array
x:ClassModifier
(to set visibility of the C# class)x:FieldModifier
- Used to set visibility of a named sub-element of the root
- Note: named element is one defined with the
Name
XAML keyword
x:Name
x:Type
- Others
- References the
- External assemblies
- Must create custom XML namespace to reference
- Use
clr-namespace
andassembly
tokens - Syntax:
xmlns:myCtrls="clr-namespace:MyControls;
assembly=MyControls" - The
clr-namespace
token is assigned to the .NET namespace in the assembly - The
assembly
token is assigned to the friendly name of the*.dll
assembly
- Controlling class & member variable declarations
- Defaults
- Type (i.e., class) definitions:
public
- Members (i.e., fields):
internal
- Type (i.e., class) definitions:
- Note:
x:ClassModifier
andx:FieldModifier
keywords not available in Kaxaml
- Defaults
- XAML elements, XAML attributes & type converters
- XAML elements map to .NET class or structure types
- Set properties &/or events of the type via attributes declared within element's opening tag
- Apparent type mismatch:
- In C# - and in .NET in general - property values are strongly typed
- However, in XAML you are passing in
string
types for property attributes/values
- WPF ships with several type converter classes which handle matters 'invisibly'
- XAML property-element syntax
- Used to handle cases where even with type converter classes you could not represent a property value as a string
- Example: using a defined
LinearGradientBrush
for theBackground
property of, say, aButton
- Solution:
- Within element scope define an element using WPFElement.Property syntax
- Within that (sub) scope define property values
- Syntax:
<DefiningClass>
<DefiningClass.PropertyOfDefiningClass>
if needed, further sub-classes, etc., as either elements or content
</DefiningClass.PropertyOfDefiningClass>
</DefiningClass>
- XAML attached properties
- "…allows a child element to set the value for a property that is actually defined in the parent element." (Troelsen: 5th Ed., p. 1162)
- Typically used to position a UI element within a parent layout manager class
- Example:
<Canvas>
<Ellipse Canvas.Top="40" Canvas.Left="90" />
</Canvas> - This approach is by no means universally available for all types nor all properties
- Note: "…attached properties are a specialized form of a WPF-specific concept termed dependency property" (Troelsen: 5th Ed., p. 1162)
- XAML markup extensions
- Final method for setting property values
- Operates by references to external, dedicated class
- A markup extension is represented internally as a class that derives from
MarkupExtension
- Will rarely, if ever, construct a markup extension
- However, a number of XAML keywords (e.g.,
x:Array, x:Null
) are themselves, in fact, markup extensions
- Syntax:
<Element PropertyToSet = "{MarkupExtension}"/>
- Example:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CorLib="clr-namespace:System;assembly=mscorlib">
<StackPanel>
<!-- Static markup extension -->
<Label Content="{x:Static CorLib:Environment.OSVersion}" />
<Label Content="{x:Static CorLib:Environment.ProcessorCount}" />
<!-- Type markup ext: XAML version of C# typeof operator -->
<Label Content="{x:Type Button}" />
<Label Content="{x:Type CorLib:Boolean}" />
</StackPanel>
</Page>
- WPF & code-behind files
- General
- You almost always put
- Look-and-feel in XAML files
- Implementation in C# code/files
- Convention in a WPF project is to name code behind files with
*.xaml.cs
extension
- You almost always put
- Code file for a MainWindow class
- Remember to:
- Identify
MainWindow
class aspartial
- In MainWindow ctor call
InitializeComponent()
method - Remember:
InitializeComponent()
is defined within*.g.cs
file
- Identify
- Define event-handling procedures
- See Troelsen: 5th Ed., pp. 1165-6, for full mark-up & code
- Remember to:
- Code for a MyApp class
- Most required code is in the
MyApp.g.cs
file - Remember to:
- Identify
MyApp
class aspartial
- Perhaps define an
AppExit()
method
- Identify
- See Troelsen: 5th Ed., p. 1166, for full mark-up & code
- Most required code is in the
- Processing code files with msbuild.exe
- Update
*.csproj
file first - See Troelsen: 5th Ed., pp. 1166-7, for full mark-up
- Update
- General
- Kaxaml
- Code-behind files & VS
- With WPF API can load, parse & save XAML descriptions programmatically
- This capability allows one to apply skins to windows
- Requires use of
XamlReader
andXamlWriter
types - These 2 types are within
System.Windows.Markup
namespace
- Requires use of
- Approach
- Create different XAML files that, say, describe the look & feel of a
Window
type - Make sure name of each control, and name of each event handler, are identical in each files
- Create different XAML files that, say, describe the look & feel of a
- See Troelsen: 5th Ed., pp. 1167-78, for an extended example
- General
- WPF Controls
- Core WPF controls
- Note: Document Outline window in VS is especially helpful
- The WPF Ink controls
- Some ink controls, like
Stroke
&StrokeCollection
are part ofSystem.Windows.Ink
namespace - Majority of ink controls, however, are actually found in
System.Windows.Controls
namespace
- Some ink controls, like
- The WPF Document controls
- WPF common dialog boxes
- Examples:
OpenFileDialog
&SaveFileDialog
- Located in
Microsoft.Win32
namespace (PresentationFramework.dll
assembly)
- Examples:
- Controlling content layout using panels
- General
- If you place a control directly in a
Window
object (i.e., without using panels) it will be positioned dead center - Again, you in part use panels because you can only put one item as content inside a
Window
object - Can intermix panels, & place panels inside panels
- If you place a control directly in a
- Panel types
Canvas
panels- Allows for absolute positioning of UI content
- Specify positioning of embedded elements against any edge(s)
- Notes:
- If position for a UI element is not specified it will be placed in upper-left corner of canvas
- Embedded elements are not resized when canvas is resized
- Nor are embedded elements resized when a
canvas
is resized by styles or templates
Canvas
panels are most appropriate for graphical content
WrapPanel
panels- Typically define height & width of UI contained elements
- Order in which you declare/list UI items within a
WrapPanel
matters Orientation
property- Default: left-to-right
- Option:
Vertical
- Can declare default sizes of contained UI elements
- Set via
ItemWidth
&ItemHeight
properties - Can override those settings for any individual UI element (simply set the item's own width & height)
- Set via
- Typically used for a small portion of total window - in particular for Toolbar controls
StackPanel
panels- UI items get stretched to fit the size of the panel
- Default value for
Orientation
property isvertical
Grid
panels- Steps to defining a
Grid
- Define each column
- Define each row
- Define content of each cell using attached property (
Grid.Row
andGrid.Column
) syntax
- As is typical in .NET, grid cells are defined using a zero-based index/system
- Within a cell, default layout is that multiple elements are aligned in a vertical stack
- Adding splitters to a
Grid
- Define a
GridSplitter
type - Apply the
GridSplitter
control to a row a column using attached property syntax - Note: you must assign a
Width
orHeight
setting (depending on whether the splitter is attached to a row or a column) or the splitter will not be visible - When attaching a
GridSplitter
object, set height or width of the attached-to row or column toAuto
- Define a
- Steps to defining a
DockPanel
panels- Typically used as the master panel
- Use attached
Dock
property within UI elements - If you add multiple UI elements to the same edge they will dock in the order in which they are declared
- Can set
LastChildFill
for how you want to handle elements whereDockPanel.Dock
is not specified
- Enabling scrolling
- Use a
ScrollViewer
type - Place panels inside the type
- Use a
- Nesting panels (extended example - see Troelsen: 5th Ed., pp. 1195-1200)
- Using
Menu
&MenuItem
classes- When setting
Header
property using an underscore character "_" in front of a letter- Underlines that letter
- Signifies that keyboard shortcut which activates the menu item (by using the
Alt
key)
- When setting
- The
ToolBar
class- Can wrap a
ToolBar
inside aToolBarTray
element - Note:
ToolBar
type "is-a"ContentControl
- Ergo, can embed any type on its surface
- Can wrap a
- The
StatusBar
class (placeStatusBarItem
in-scope)
- Using
- General
- WPF control commands
- General
- Typically, .NET events are defined within a specific base class - ergo tightly bound to that class
- Control commands, however, can be used a across numerous control types
- Control commands are event-like entities
- The intrinsic control command objects
- A control command is "any object that supports a property (often called
Command
) that returns an object implementing theICommand
interface" (Troelsen: 5th Ed., p. 1201) - Members of the
ICommand
interface:event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter); - Could technically define custom command objects
- However, .NET ships with >100 command objects
- Classes:
ApplicationCommands
- Examples:
Close, Copy, Find, Undo
- Namespace:
System.Windows.Input
- Examples:
ComponentCommands
- Examples:
MoveDown, MoveFocusBack, ScrollToEnd
- Namespace:
System.Windows.Input
- Examples:
MediaCommands
- Examples:
BoostBase, NextTrack, Play, Rewind
- Namespace:
System.Windows.Input
- Examples:
NavigationCommands
- Examples:
BrowseBack, Favorites, NextPage, Zoom
- Namespace:
System.Windows.Input
- Examples:
EditingCommands
- Examples:
AlignCenter, CorrectSpellingError, MoveDownByLine
- Namespace:
System.Windows.Documents
- Examples:
- A control command is "any object that supports a property (often called
- Connecting commands to the
Command
property- Many UI elements have built-in support for the
Command
property - Set value of the
Command
property to aCommandObject
- Syntax/example (attaching commands to an
Edit
menu header):
<MenuItem Header="_Edit">
<MenuItem Command="ApplicationCommands.Copy" />
<MenuItem Command="ApplicationCommands.Cut" />
<MenuItem Command="ApplicationCommands.Paste" />
</MenuItem> - Once connected, control commands are accessible via standard keyboard shortcuts
- Control commands are also often accessible in standard, short-cut menus (i.e., via right-clicking the mouse)
- Many UI elements have built-in support for the
- Connecting commands to arbitrary actions
- Must often employ procedural code if you want to attach a command object to an application-specific event
- Must declare a
CommandBinding
object- Container/parent object will have a
CommandBindings
collection - In procedure code, after declaring a
CommandBinding
object runCommandBindings.Add()
- In XAML mark-up add
CommandBinding
objects by declaring them via property-element syntax (i.e., withinelement.CommandBindings
scope)
- Container/parent object will have a
- A
CommandBinding
object implements theICommand
interface - Must then define the
CanExecute
andExecute
methods - See Troelsen: 5th Ed., pp. 1203-6, for coding sample
- General
- Using Expression Blend (see Troelsen: 5th Ed., pp. 1207-27, for an extended example)
- The
TabControl
- The
RadioButton
control - The
InkCanvas
control- Contains an
EditingMode
property EditingMode
property takes enumeratedInkCanvasEditingMode
valuesInk
modeSelect
modeEraseByStroke
mode
- Each
Stroke
is contained in aStrokeCollection
object - Controlling the pen:
DefaultDrawingAttributes
property returns aDrawingAttributes
object- Use
DrawingAttributes
object to set color, size, other properties
- Contains an
- The
ComboBox
control- Three ways to determine selected item in a
ComboBox
orListBox
control- Use
SelectedIndex
property- Zero-based
- Value of
-1
means no selection was made
SelectedItem
returns the object selected- Get the value of the selected object
- Typically, you use the returned object's
ToString()
method - Capture this in the
SelectedValue
property
- Typically, you use the returned object's
- Use
- Note: you do not have to make the contained elements
ComboBoxItems
- Could make members, say, individual
StackPanel
objects- If doing something along these lines
Tag
property is much likeid
in html Tag
is a quick & dirty way to determine the selected item
- If doing something along these lines
- Three ways to determine selected item in a
- Saving, Loading & clearing
InkCanvas
data- Will need to import
System.IO
andSystem.Windows.Ink
namespaces - See Troelsen: 5th Ed., pp. 1226-7, for code sample
- Will need to import
- The
- The documents API
- Block & inline elements
- Block elements
- From
System.Windows.Documents.Block
base class - Used to group together other content
- Examples:
List
Paragraph
BlockUIContainer
Section
Table
- From
- Inline elements
- From
System.Windows.Documents.Inline
base class - Examples:
Run
Span
LineBreak
Figure
Floater
Bold
Italic
- Example (using
inlines
in code):
Bold b = new Bold;
Run mrMgr = new Run("Wow. I'm Mr. Manager.");
mrMgr.Foreground = Brushes.Red;
b.Inlines.Add(mrMgr);
- From
- Block elements
- Document layout managers
- Document elements do not go directly inside a layout panel
- Must embed inline and block document elements within a
FlowDocument
orFixedDocument
element FixedDocument
container gives*.pdf
-style, locked layoutFlowDocument
- Allows user to rearrange lay-out
- A
FlowDocument
itself goes inside 1 of 4 XPS-aware layout managers:FlowDocumentReader
- Allows zooming & searching
- Content can be laid out in various forms
FlowDocumentScrollViewer
- No zooming, searching, etc.
- Presents a single document with scroll bars
RichTextBox
- allows user editingFlowDocumentPageViewer
- Data can be zoomed but not searched
- Data is presented 1 page at a time
- Block & inline elements
- Building the documents tab
- Populating a
FlowDocument
using codeRun
objects (newable)- Use to declare simple chunks of text
- Embed
Run
objects inside paragraphs, lists, etc. Run
objects have any number of formatting properties
Lists
- Populate using
Add()
method ofListItems
collection - Each member of
ListItems
collection must be itself aListItem
object - A
ListItem
itself can in turn be, say, aparagraph
- Example:
this.geoBluthCrimes.ListItems.Add(
new ListItem(new Paragraph(
new Run("Light treason"))));
- Populate using
- Enabling annotations & sticky notes
- Need to add a custom namespace to use:
xmlns:a="clr-namespace:System.Windows.Annotations; assembly=PresentationFramework"
- XAML Syntax:
<Button Content="Some text" Command="a:AnnotationService.CreateStickyNoteCommand" />
- In C# code
- Required namespaces:
System.Windows.Annotations
System.Windows.Annotations.Storage
- Types to declare/use:
AnnotationService
MemoryStream
AnnotationService
(declare as a newXmlStreamStore
, into which you pass the AnnotationService object
- Then, run
Enable()
on theAnnotationService
object, passing in theAnnotationStore
object
- Required namespaces:
- Need to add a custom namespace to use:
- Saving & loading a flow document
- Use
XamlReader
&XamlWriter
types (inSystem.Windows.Markup
namespace) - See Troelsen: 5th Ed., pp. 1234-5, for how to code
- Use
- Populating a
- The WPF data-binding model
- General
- Data binding means hooking up a control's property to the value of some application variable
- With data binding must distinguish between the source & the destination
- WPF has its own data-binding architecture
- You do not have to use that architecture - though it makes little sense to code for data binding yourself
- Establishing data bindings using Blend (see Troelsen: 5th Ed., pp. 1236-8)
- Use the
{Binding}
markup extension - Can, of course, create the markup by hand
- Blend, however, allows you to work with the window to set the binding & markup
- Example (binding to the
Content
of a control):- Open the window of the control you are working with
- Click on the very small white square next to the property
- Click on the tab
- Click on
- Navigate to the appropriate element & property
- Use the
- The
DataContext
property- Provides an alternate format for marking up data binding
- Use
DataContext
property to define the element or object you are binding to - Then use, say,
Content
to set the property of that element or object - Example (to bind to the value of a
SliderBar
):
<Label DataContext="{Binding ElementName=mySB}"
Content="{Binding Path=Value}" />
- Use
- Advantages of using
DataContext
property- Can set the
DataContext
property in a parent element - Within child elements could then get different properties from the one source
- Alternative, within child elements could get same property but employ that property in different ways
- See Troelsen: 5th Ed., p. 1239, for an example of this alternative approach
- Can set the
- Provides an alternate format for marking up data binding
- Data conversion using
IValue
converter- Interface is part of
System.Windows.Data
namespace - Need to create a converter class
- Use this if your raw data is, say, a
double
but you want/need anint
- Class provides two methods (for two-way data binding)
Convert
- for values transformed from source to destinationConvertBack
- visa versa
- Interface is part of
- Establishing data bindings in code
- The
IValueConverter
type will have to be called upon in code - Remove any
{Binding}
extension method declarations from your XAML mark-up - Invoke the
SetBinding()
method of the appropriate control - See Troelsen: 5th Ed., pp. 1241-2, for sample code
- The
- Building a
DataGrid
tab- Define a
DataGrid
inside aStackPanel
- Syntax/example:
<TabItem x:Name="bluthsDataGrid" Header="Bluths">
<StackPanel>
<DataGrid x:Name="gridBluths" Height="200" />
</StackPanel>
</TabItem> - Assuming you have created & referenced a DAL
*.dll
(using EF) you fill theDataGrid
via code - Syntax/example:
private void ConfigureGrid()
{
using (BluthEntities context = new BluthEntities())
{
// Get Bluth data via LINQ query…
var bluths = from b in context.BluthEmployees
select new { b.LastName, b.FirstName, b.DOB, b.salary };
this.gridBluths.ItemsSource = bluths;
}
}
- Define a
- General
- Core WPF controls
- WPF Graphics Rendering Services
- WPF's graphical rendering services
- General
- WPF uses retained-mode graphics
- Means WPF essentially "remembers" visuals, even when hidden
- Therefore, WPF responsible for redrawing visuals when needed
- Other methodology, used in Windows Form's GDI, is immediate-mode graphics
- Here, programmer's responsibility to "remember" graphics
- Programmer had to worry about repositioning when containing element was resized
- WPF uses retained-mode graphics
- WPF graphical rendering options
- Shapes
- In
System.Windows.Shapes
namespace - Handful of basic shapes
- Easy to use but have lots of memory overhead
- Best for interactive graphical objects
- In
- Drawings & geometries
- These are inherited from
System.Windows.Media.Drawing
abstract class - Examples:
GeometryDrawing, ImageDrawing
- Fewer features, but less memory-intensive
- These are inherited from
- Visuals
- Only accessible through C# code
- Descendants of
System.Windows.Media.Visual
namespace - When using you are effectively communicating directly with WPF graphical subsystem
- Least memory-intensive
- Shapes
- General
- Shapes
- General
System.Windows.Shapes
namespace found inPresentationFramework.dll
assembly- Will also make use of types in
System.Windows.Media
namespace (e.g.,HitTestResult, VisualTreeHelper
) - 6 sealed classes (each extends the abstract base
Shape
class):Ellipse
Rectangle
Line
Polygon
Polyline
Path
- Shapes inheritance chain(s) give them a lot of functionality
- Inherit from
UIElement
, so:- Can receive mouse input
- Can respond to drag-and-drop events
- Other functionality
- Inherit from
FrameworkElement
, so:- Can respond to sizing
- Can control mouse cursors
- Other functionality
- Effectively, shapes behave just like a normal WPF control (e.g.,
Button
)
- Inherit from
- Shapes have their own, additional properties
DefiningGeometry
- Returns the overall dimension of a shape
- This is lightweight because it comes with no inherent functionality
Fill
(for specifying a brush object)GeometryTransform
- Applies a transformation before the shape is rendered on the screen
- The inherited
RenderTransform
property is used after a shape is rendered
Stretch
- Controls how a shape fills its allocated space within a layout manager
- Property value must be of the
System.Windows.Media.Stretch
enumeration
Stroke
- Takes either a brush or a pen object
- Used to paint the border of a shape
StrokeDashArray, StrokeEndLineCap, StrokeStartLineCap
&StrokeThickness
(& others)- Control how lines are configured when drawing the border of a shape
- These generally configure the brush itself that is used to draw the shape's border
- Adding rectangles, ellipses, & lines to a canvas (see Troelsen: 5th Ed., pp. 1249-52, for code example)
Line
shape- Has (obviously) no properties for height and width
- Instead, use
X1,Y1
properties to set starting point andX2,Y2
properties for ending point
Rectangle
shape hasRadiusX, RadiusY
properties for rounding cornersCanvas
object(s)SetLeft, SetTop
, etc. methods set theCanvas.Left, Canvas.Top
, etc. properties of dependency objects- Use
someCanvasObject.Children.Add()
method to add items to canvas
- Removing rectangles, ellipses, & lines from a canvas (see Troelsen: 5th Ed., pp. 1252-3, for code example)
- Requires use of hit-test logic
- Need to get coordinates of where user clicked
- Syntax:
Point pt = e.GetPosition((Canvas)sender);
- Note: cast
sender
parameter asCanvas
object
- Also requires interaction with Visual Tree(s)
- Use
HitTestResult
&VisualTreeHelper
types:
HitTestResult r =
VisualTreeHelper.HitTest(someCanvasObject, somePoint); VisualTreeHelper.HitTest()
returns the top-mostUIElement
clicked on - not on any objects underneath
- Use
- Requires use of hit-test logic
Polyline
&Polygon
objects- In markup, use
Points
property to define- Property takes sets of space-separated X-Y points
- Those individual points are comma-separated pairs
- At least in Kaxaml, each x-value represents offset from left edge & y-value represents offset from top-edge
Polyline
- An unclosed shape
- Can set
StrokeLineJoin
property to control how joints are displayed (e.g.,Round
)
Polygon
- A closed shape
- Can therefore set
Fill
property
- In markup, use
Path
types- General
- Allows you to combine geometries
- Geometries are assigned to
Data
property ofPath
class- Attribute values must be a
System.Windows.Media.Geometry
-derived class - Examples:
LineGeometry, RectangleGeometry,
etc. - Members of
System.Windows.Media.Geometry
-derived classesBounds
FillContains()
(used with hit-testing calculations)GetArea()
GetRenderBounds()
Transform
- Syntax:
<Path Fill="Red">
<Path.Data>
<GeometryGroup>
<EllipseGeometry propertiesblahblah />
<RectangleGeometry propertiesblahblah />
<LineGeometry propertiesblahblah />
</GeometryGroup>
</Path.Data>
</Path> - Differences between
Geometry
-derived classes andShape
-derived classesGeometry
-derived classes are notUI
-derived classes & therefore cannot render themselvesGeometry
-derived classes essentially a just a collection of plot-point data
- There are other WPF classes, besides
Path
, that can use geometries for rendering
- Attribute values must be a
- The Path Modeling Mini Language
- Used with
PathGeometry
types- Strings together objects that represent segments and figures
- Component examples:
ArcSegment, BezierSegment, LineSegment
- Markup sample: see Troelsen: 5th Ed., p. 1256
- Mini-Language
- Allows segments to be condensed into a value assigned to the
Data
property of aPath
element - Mini-language is used automatically by Expression tools
- See Path Markup Syntax in .NET Framework 4.0 SDK documentation for details
- Allows segments to be condensed into a value assigned to the
- Used with
- General
- General
- WPF brushes & pens
Brush
-derived typesDrawingBrush
(used withDrawing
-derived objects)ImageBrush
- Paints an area with an image
- Use with
ImageSource
object
LinearGradiantBrush
RadialGradiantBrush
SolidColorBrush
(set with theColor
property)VisualBrush
(paints an area with aVisual
-derived object)
- Configuring brushes using VS (see Troelsen: 5th Ed., pp. 1258-60)
- Configuring brushes in code (see Troelsen: 5th Ed., pp. 1260-1)
- When converting brush hexadecimal values to
Color
object useSystem.Windows.Media.ColorConverter
class - Note: Remember to use hash/pound sign inside quotation marks when employing
ConvertFromString
method - Can also use:
-
Colors
enumerations Color
object (e.g.,Color myColor = new Color() {…};
)
-
- When converting brush hexadecimal values to
- Configuring pens
- Used for drawing borders of geometries
- Also used for
Line
&PolyLine
classes themselves - Has same sort of properties as a
Shape
class - Typically you only create a
Pen
indirectly, as when specifyingStrokeThickness
- Creating
Pen
object can be helpful when working withDrawing
-derived types
- Applying graphical transformations
- General
- Derive from the
System.Windows.Media.Transform
abstract base class - Transformations can be applied to any
UIElement
(e.g.,Shape
descendants,Buttons, TextBoxes
, etc.) - Transformations applied to targets via one of two properties:
LayoutTransform
- This is applied before the element is rendered
- Ergo, does not affect z-order
- Another way of saying transformed image data will not overlap
RenderTransform
- Applied after the element is rendered
- Transformed image data may overlap (which would affect z-order)
Transform
-derived types (for 2D types)MatrixTransform
RotateTransform
ScaleTransform
SkewTransform
TranslateTransform
TransformGroup
- represents a composite ofTransform
types
- Syntax:
<SomeElement…>
<SomeElement.LayoutTransform>
<RotateTransform… />
</SomeElement.LayoutTransform>
</SomeElement>
- Derive from the
- Transforming canvas data
- In addition to single items can apply transformations to layout managers
- The transformation applies to all contained items
ClipToBounds
property- Setting to true will keep, say, rotated units from being rendered off a canvas (i.e., on menu bars, etc.)
- Rotated items would effectively disappear
- Note: when applying transformations to a canvas:
- If you then want to add a shape by, say, clicking on the canvas, your mouse coordinates are now all "messed up"
- Handle that by working with
Canvas.SetLeft
&Canvas.SetTop
properties - See Troelsen: 5th Ed., pp. 1265-6, for sample workaround
- General
- Working with shapes using Expression Blend (see Troelsen: 5th Ed., pp. 1266-71)
- Converting shapes to paths (Path/Convert to Path short-cut menu)
- Combining shapes (Combine short-cut menu)
- The brush & transformation editors
- Rendering graphical data using drawings & geometries
- General
- These inherit from
System.Windows.Media.Drawing
class (inPresentationCore.dll
) - Neither
UIElement
norFrameworkElement
are in the inheritance chain - Limitations with
Drawing
objects- Cannot handle input events
- Cannot render themselves - must be placed inside a hosting object:
DrawingImage
(which in turn is placed inside an image control)DrawingBrush
DrawingVisual
(controlled entirely by C# code, not by XAML)
- What can be done with
Drawing
-derived objects- Perform hit-testing logic
- Animations (because they extend
Animatible
)
- Derived types:
DrawingGroup
(used to combine multiple objects into one)GeometryDrawing
GlyphRunDrawing
(used for textual data)ImageDrawing
(places an image inside a bounding rectangle)VideoDrawing
- Handles either audio or video files
- For video,
MediaPlayer
type generally works better
- These inherit from
- Building a
DrawingBrush
using geometries- Syntax:
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing>
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry … >
etc. - See Troelsen: 5th Ed., pp. 1272-3, for full sample markup
- Note: because you are not inheriting from
Shape
class you have neitherStroke
norFill
properties- Create a
Pen
element to draw boundaries - Create a
Brush
element to fill with color
- Create a
- Syntax:
- Painting with the
DrawingBrush
(see Troelsen: 5th Ed., pp. 1273-4, for sample markup) - Containing drawing types in a
DrawingImage
(see Troelsen: 5th Ed., pp. 1274-5, for sample markup)
- General
- Generating complex vector graphics using Expression Design
- Can exporting a design document from Design to XAML (File/Export)
- After exporting simply copy the XAML markup (the
Canvas
object) into a project - Will see lots of
Path
elements
- Rendering graphical data using the visual layer
- General
- Lightweight classes, with low memory overhead
- Visual layer can be especially useful when rendering a single image over a large area
- The
Visual
base class & derived child classes- Base class (abstract) is
System.Windows.Media.Visual
- Derived classes:
DrawingVisual
(the class most likely to use)Viewport3DVisual
ContainerVisual
- Base class (abstract) is
- Using the
DrawingVisual
class (basic)- Used to render shapes, images, & text
- Minimal required steps:
- Work with/create a
DrawingContext
object from theDrawingVisual
- Render the content via the
DrawingContext
- Work with/create a
- To work with hit-testing will also need to do the following:
- "Update the logical and visual maintained by the container you are rendering on top of." (Troelsen: 5th Ed., p. 1278)
- Override 2 virtual methods from the
FrameworkElement
class:GetVisualChild()
method- The read-only
VisualChildrenCount
property - Both of these are called internally during the rendering process
- Steps
- Create an
Image
item in markup, but do not specify aSource
(handle that in the code-behind) - Specify
Source
in the code-behind for theWindow_Loaded()
event-handler- Create a
DrawingContext
object via a call to theRenderOpen()
method of aDrawingVisual
object - Note:
DrawingContext
class implementsIDisposable
, soNew
the object within ausing
block/keyword - Inside the
using
block implement methods such asmyDrawingContext.DrawRoundedRectangle
, etc. - Create a *.bmp file (for example) via new-ing
RenderTargetBitmap
object - Set that *.bmp file as the
Source
for theImage
element created in markup
- Create a
- Create an
- See Troelsen: 5th Ed., pp. 1278-9, for sample code-behind
- Rendering visual data to a custom layout manager
- More common to use a
DrawingVisual
to do this than to create a background for WPF control - Basic steps:
- Create a class (in code) extending the
FrameworkElement
type- In Solution Explorer add a C# class
- Import
System.Windows, System.Windows.Input,
&System.Windows.Media
namespaces
- Inside the class create a member variable of type
VisualCollection
- The
VisualCollection
type is a container type to which you add objects of typeVisual
- Plug the
FrameworkElement
-derived object into aWindow, Page
, orUserControl
- Create a class (in code) extending the
- You lose some functionality but you do retain the ability to render visuals
- Advantage: you use a highly optimized rendering agent
- Remember to override the property & method mentioned above
- In XAML mark-up remember to add XML namespace reference to created class/type
- See Troelsen: 5th Ed., pp. 1280-2, for sample code & markup
- More common to use a
- Responding to hit-test operations
- Ability to respond to hit-test operations must be added in programmatically
- This depends on the distinction between logical trees & visual trees
- Must register custom visuals with these data structures
- This is done automatically with the
VisualContainer
collection - Note: This is why we new a
VisualContainer
within the custom layout manager's constructor
- This all gets very complicated. See Troelsen: 5th Ed., pp. 1282-3, for sample markup
- General
- WPF's graphical rendering services
- WPF Resources, Animations, & Styles
- The WPF resource system
- General
- Binary resources are image files, audio clips, etc.
- Logical resources (also called object resources) are reusable .NET objects
- Can be used throughout a WPF application
- Generally these are graphical resources
- Binary resources
- Including loose resource files in a project
- Loose resources are files included/created in a sub-directory of the application's root file
- In Solution Explorer window of VS create a sub-directory (e.g., Images)
- Add desired files to folder
- On short-cut menu for folder select
- Configuring loose resources
- You ultimately want files copied to
\bin\Debug
folder - In your, say, Images folder, select all files & open
window
- Set
BuildAction
property toContent
- Set
Copy to Output Directory
property toCopy always
- Set
- Run
\bin\Debug
folder & files will be copied to
- You ultimately want files copied to
- Programmatically loading an image
- Use
BitmapImage
class, which is inSystem.Windows.Media.Imaging
namespace - When working with multiple images load them into a
List<T>
ofBitmapImages
- Note: A prudent programmer always puts file/image retrieval code inside a
Try/Catch
block - See Troelsen: 5th Ed., pp. 1288-9, for full code sample
- Use
- Embedding application resources
- You also have the option of embedding image right within the project's binary
- One advantage: not having to search in destination folders for them in code
- On each file open Properties window
- Set
BuildAction
property toResource
- Set
Copy to Output Directory
property toDo not copy
- If you initially included as loose resources run to remove old files
- Set
- Note: When setting
Uri
definition specifyUriKind.Relative
in ctor - See Troelsen: 5th Ed., pp. 1289-91, for full code sample
- Including loose resource files in a project
- Object (logical) resources
- The
Resources
property- Property is in
FrameworkElement
base class Application
class does not descend fromFrameworkElement
but has its ownResources
propertyResources
property encapsulates aResourceDictionary
objectResourceDictionary
accepts all objectsResourceDictionary
can be manipulated in either markup or code- As a dictionary, you give a
Resource
aKey
value (usex:Key
)
- Property is in
- Defining window-wide
Resources
- Can place a
ResourceDictionary
within any (FrameworkElement
-derived) type, and thatResource
is then available to all child elements - Promoting an element to a
Resource
:- In VS, open window of parent element, click on small white diamond next to property you want to promote
- In Blend click on the button (small white dot) of the property you want to promote
- Can place a
- Utilizing
Resources
- The
{StaticResource}
markup extension- Use the
{StaticResource}
markup extension to define the appropriate property - Syntax (assuming
Brush Resource
for aButton
):
<Button Background="{StaticResource someBrushResourceKey}" />
- Use the
- You can always change a resource after extracting it
- The
{DynamicResource}
markup extension- You can change properties of a
Resource
in code- You will throw a runtime exception if you unsuccessfully attempt to access a
Resource
by its key name TryFindResource()
is a better approach; it returnsnull
if theResource
is not found
- You will throw a runtime exception if you unsuccessfully attempt to access a
- However, if you programmatically change the underlying object of the
Resource
- A reference to that
Resource
using{StaticResource}
markup extension will not detect the change - A reference to that
Resource
using{DynamicResource}
markup extension will detect the change
- A reference to that
- The
{DynamicResource}
markup extension, however, utilizes more resources, so use sparingly
- You can change properties of a
- To apply a
Resource
in VS, click on square within appropriate property- Open window for appropriate element
- Navigate to desired property & then click on square
- The
- Application-level resources
- Cannot promote
Resources
toApplication
level automatically within VS - Workaround: cut & paste the markup
- Cannot promote
- Defining merged resource dictionaries
- Used when you have a
Resource
you want to access across multiple WPF projects - Merged resource dictionary = a
*.xaml
file which contains nothing but a collection of object resources - Single project can contain several such files
- Typically place, say, brushes in one dictionary, animations in another, etc.
- Add each dictionary via the dialog box in VS's menu
- Root element is
ResourceDirectory
- File must be added to the home project
- Resource-only assembly must then be merged into project, typically at
Application
level - Syntax (to merge
ResourceDictionary
):
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyBrushes.xaml"/>
closing tags…
- Used when you have a
- Defining a resource-only assembly
- Easiest approach to create:
- Add a WPF User Control Library project to existing Solution
- Delete the auto-created
*.xaml
file - Drag existing
ResourceDirectory *.xaml
file into User Control Library - (Delete the file from existing Project after moving/copying it)
- Compile the Solution
- To access the assembly after compiling:
- Must redefine
Source
property ofResourceDictionary
- Syntax:
Source="/nameOfAssembly;Component/nameOfXamlFileInAssembly.xaml" - Note: That syntax is white-space-sensitive
- Must redefine
- Easiest approach to create:
- The
- General
- WPF's animation services
Animation
class types- 100+ class types within
System.Windows.Media.Animation
namespace (inPresentationCore.dll
assembly) - Three broad types:
someDataTypeAnimation
types (e.g.,ByteAnimation, ColorAnimation
, etc.)- These allow you to work with linear interpolation animations
- In English, allows you to change a value smoothly over time
someDataTypeAnimationUsingKeyFrames
types (e.g.,StringAnimationUsingKeyFrames
, etc.)- These represent key frame animations
- Use these to cycle through a given set of values over time (e.g., the text on a
Button
)
someDataTypeAnimationUsingKeyPath
types- Used to move items along a defined
Path
- These types are used in, say, GPS applications
- Used to move items along a defined
- Animation types are connected to dependency properties of a given object
- Dependency properties are static & read-only
- Dependency properties identified by appending the word
Property
to the normal property name - Example: when animating the height of a
Button
work withHeightProperty
rather thanHeight
- Since
Height
(i.e.,HeightProperty
) takes adouble
you work withDoubleAnimation
type
- 100+ class types within
- The
To, From
&By
properties- These are all properties of the animation base classes
- To: the animation's ending value
- From: the animation's starting value
- By: the total amount by which the animation changes the starting value
- Note: These are not, however, virtual properties of a base class
- That is because the underlying types that
To, From,
&By
accept are, of course, all over the map - The variety of types is also (partly) why there is not a single generic animation class (e.g.,
Animate<T>
)
- That is because the underlying types that
Timeline
base class- While there is no, single base
Animation
class there is a singleSystem.Windows.Media.Animation.Timeline
base class - Properties:
AccelerationRatio, DecelerationRatio
&SpeedRatio
(used to control pacing)AutoReverse
(the default value isfalse
)BeginTime
(the default value is 0, which starts the animation immediately)Duration
FillBehavior, RepeatBehavior
(controls what the animation does once the timeline has elapsed)
- While there is no, single base
- Authoring an animation in C# code
- Note: Generally animation is authored entirely in XAML
- In appropriate event-handler code declare & instantiate an
Animation
object (e.g.,DoubleAnimation
) - Probably want to set a
Window
class-levelbool
type to control/track whether animation is running or not - Toggle the boolean via lambda statement on the animation's
Completed
property - Create a
Transform
-derived object (e.g.,RotateTransform
) & set it as the value of the appropriate element'sRenderTransform
(orLayoutTransform
) property - Invoke the
BeginAnimation()
method of theTransform
-derived object, passing in 2 parameters- The dependency property to be animated
- Relevant
Animation
object
- Controlling the pacing of an animation
- Animations generally complete in 1 second
- Use
Duration
property of anAnimation
to change that - Typical syntax:
myAnimation.Duration=(TimeSpan.FromSeconds((4));
- Reversing (
AutoReverse
property) & looping (RepeatBehavior
property) an animation- Can set
RepeatBehavior
property toRepeatBehavior.Forever
- Syntax to hard-code number of loops in animation:
someAnimObject.RepeatBehavior = new RepeatBehavior(3);
- Syntax to control time for animation to repeat:
someAnimObject.RepeatBehavior =
new RepeatBehavior(TimeSpan.FromSeconds(30));
- Can set
- Authoring animations in XAML
- General
- In XAML, as with C#, you still create & configure an
Animation
object - "One big difference, however, is that WPF is not function-call friendly." (Troelsen: 5th Ed., p. 1309)
- Therefore, instead of invoking
BeginAnimation()
method you employ aStoryboard
- In XAML, as with C#, you still create & configure an
Storyboards
Storyboard
elements are used to map child animation object to a property of a parent element viaTargetProperty
property- Syntax:
<Storyboard TargetProperty = "FontSize">
<someAnimation … />
</Storyboard> Storyboard
element itself is always wrapped insideBeginStoryboard
element tags
EventTriggers
- A trigger is something of a trick to enable XAML to handle events sans procedural code
- To trigger an animation simply wrap a
Storyboard
element (and its children) inside aEventTrigger
element- Set the
RoutedEvent
property inside theEventTrigger
opening element - Between
EventTrigger
&BeginStoryBoard
elements can often splice inEventTrigger.Actions
property-element - An
EventTrigger
element is itself placed inside a<SomeElement.Triggers>
collection
- Set the
- Syntax/example:
<Button Content="OK">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard TargetProperty="FontSize">
<DoubleAnimation From="12" To="24"
Duration="0:0:3" AutoReverse="True" />
</Storyboard TargetProperty="FontSize">
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
- Animation Using Discrete Key Frames
- These create a collection of specific values for an animation that should be invoked at specific times
- Inside
Storyboard
element use axyzAnimationUsingKeyFrames
element - Inside that use individual
DiscretexyzKeyFrame
elements - Syntax/example:
<StackPanel>
<Button Name="myButton" setOtherPpts…>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Loaded">
<BeginStoryboard>
<Storyboard>
<StringAnimationUsingKeyFrames
RepeatBehavior="Forever"
Storyboard.TargetName="myButton"
Storyboard.TargetProperty="Content"
Duration="0:0:3">
<DiscreteStringKeyFrame Value="" KeyTime="0:0:0" />
<DiscreteStringKeyFrame Value="O" KeyTime="0:0:1" />
<DiscreteStringKeyFrame Value="OK" KeyTime="0:0:1.5" />
<DiscreteStringKeyFrame Value="OK!" KeyTime="0:0:2" />
</StringAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger RoutedEvent="Button.Loaded">
</Button.Triggers>
</Button>
</StackPanel>
- General
- WPF styles
- General
- "Simply put, a WPF style is an object that maintains a collection of property/value pairs." (Troelsen: 5th Ed., p. 1312)
- Class:
System.Windows.Style
- Key properties:
Setters
(default property)- This is a container property of strongly-typed
Setter
objects - Use
Setter
objects to define property/value pairs
- This is a container property of strongly-typed
Triggers
- used to capture event triggers in aStyle
BasedOn
- used to create a newStyle
from an existing oneTargetType
- used to constrain where theStyle
can be applied
- Defining & applying a
Style
- General
Styles
are almost always packaged as object resources- To use a
Style
in an element- Call the
Style
property of the element where you want theStyle
applied - Must use either
{StaticResource}
or{DynamicResource}
markup extension
- Call the
- Syntax for more complicated property/value settings (example):
<Setter Property="RenderTransform">
<Setter.Value>
<RotatateTransform Angle="20"/>
</Setter.Value>
</Setter>
- Overriding style settings
- Simply specify the value of the property you want to override
Style
property is always read first; specific properties next
- Automatically applying a style with
TargetType
- When specifying a
TargetType
the style will then apply to all children of theTargetType
- If a style includes a property not supported by a derived type, that type will simply ignore the specified dependency property
- You still must specify the
Style
property inside a given element, using markup extension syntax, to have the style apply
- When specifying a
- Subclassing existing styles
- Use
BasedOn
property - Style being referenced must have been given a proper
x:Key
value - Use
{StaticResource}
markup extension syntax to define the referenced base style - Can both override & add to properties in base style
- Use
- Unnamed styles
- Styles which do not have
x:Name
specified will apply to all elements specified by itsTargetType
- For individual elements on which you do not want a default/unnamed style to apply set that element's
Style
property to{x:Null}
- Styles which do not have
- General
- Working with Triggers
- Defining styles with
Triggers
- You can include
Trigger
objects insideStyle
objects - This avoids the need to write any C# event-handling code
- The appropriate
Style
is activated when theTrigger
condition isTrue
- When the
Trigger
condition is no longerTrue
the element will revert to its 'default' style - Syntax/example:
<!-- Set a default style for all text boxes -->
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="14" />
<!-- Set other properties… -->
<!-- Change a ppty when text box is in focus -->
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="FontSize" Value="18" />
</Trigger>
</Style.Triggers>
</Style>
- You can include
- Defining styles with multiple
Triggers
- With multiple triggers all conditions must true for the style to be applied
- Syntax:
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="someProperty" Value="someVal"/>
…
</MultiTrigger.Conditions>
<Setter Property= … />
- Defining styles with
- Animated styles
- Can use triggers to initiate an animation sequence
- Syntax/example (making a button grow when a mouse is inside the surface area):
<Style x:key="GrowingBtnStyle" TargetType="Button">
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="100" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryBoard>
<Storyboard TargetProperty="Height">
<DoubleAnimation From="40" To="200"
Duration="0:0:2" AutoReverse="True" />
</Storyboard>
</BeginStoryBoard>
<Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style> - To perform additional actions in this example would define a
<Trigger.ExitActions>
scope, & have those triggered whenIsMouseOver="False"
- Assigning styles programmatically
- Where this can be useful
- You want to allow user to set their own UI look & feel
- You need to control appearance based on, say, security settings
- Possible set-up
- Give user a
ListBox
of available styles - For
SelectionChanged
property of list box define a method such as"comboStyles_Changed"
- In code fill the
ListBox
with selections corresponding to thex:Key
values you have assigned to the various available styles - Define event-handler as follow:
private void comboStyles_Changed(object sender,
SelectionChangedEventArgs e)
{
// Get selected style name from list box, cast that to a Style…
Style currStyle = (Style)
TryFindResource(lstStyles.SelectedValue);
if (currStyle != null)
{ this.btnStyle.Style = currStyle; }
}
- Give user a
- Where this can be useful
- General
- Generating styles with Expression Blend & default visual styles
- Blend ships with a number of default styles for the more common controls
- These are called Simple Styles
- These styles are in the library
- Click on on left-hand panel to view
- Selecting/clicking on one of the Simple Styles triggers the following…
- The appropriate control is declared with the
{DynamicResource}
markup extension - Blend adds a file entitled
Simple Styles.xaml
, which is visible/available under window
- The appropriate control is declared with the
- Working with
Simple Styles.xaml
file- Can double-click to open
- The file is a large
<ResourceDirectory>
of default control styles - Open
window in Blend
- Each style is listed
- Click on any style to edit it in a designer window
Simple Styles.xaml
gets merged into your app's resources, which is a huge plus- All edits to these styles are compiled into your app
- "Taking advantage of default simple styles essentially allows you to use starter markup for a control's normal look and feel, and customize it on a per-project basis." (Troelsen: 5th Ed., p. 1323)
- The WPF resource system
- WPF Control Templates & UserControls
- Dependency Properties
- General
- Only other place dependency properties are seen inside .NET universe is in Windows Workflow Foundation
- Dependency properties often indistinguishable from normal properties - esp. to users
- Interestingly,
Height, Width
&Content
properties are typically in fact dependency properties - How to declare a dependency property
- The encapsulating class must inherit from
DependencyObject
- Note: both
FrameworkElement
&ContentControl
inherit fromDependencyObject
- The property itself must be:
- Public
- Static
- Read-only
- Note: By convention you append
Property
to the name of the dependency property
- You register a
DependencyProperty
variable through a static call toDependencyProperty.Register()
, which occurs in either:- Static constructor
- Inline during variable declaration
- Note: behind-the-scenes activity of the encapsulating class when a dependency property is declared:
- Encapsulating class "will define a XAML-friendly CLR property…" (Troelsen: 5th Ed., p. 1326)
- This property in turn "makes calls to methods provided by
DependencyObject
to get and set the value." (Troelsen: 5th Ed., p. 1326)
- The encapsulating class must inherit from
- Benefits/features of dependency properties
- Can inherit values from a parent element's XAML definition (e.g., if you define
FontSize
in an opening<Window>
tag all elements inside the window inherit that property) - Values of dependency properties can be set by child XAML elements
- Values can be determined by WPF by references to multiple external values, which is critical for
- Animation
- Data binding (e.g., must pass in a dependency property as a parameter when invoking a control's
SetBinding()
method)
- Support for WPF triggers - which, in turn, are also frequently used for:
- Animation
- Data binding
- XAML wrapper allows you to interact with a dependency property in same way you would with a normal CLR property
- Can inherit values from a parent element's XAML definition (e.g., if you define
- Building a custom control
- This is the only time you need to build a dependency property
- Dependency properties required if the control…
- Will be the target of data binding or an animation operation
- Must broadcast that it has changed
- Needs to work as a
Setter
in a WPF style - Needs to receive values from a parent element
- Best Practice: Always create a custom control's properties as dependency properties
- Syntax/example (
FrameworkElement
, which is aDependencyObject
, with some code omitted)
public class FrameworkElement : UIElement, 5 other inheritances…
{
…
public static readonly DependencyProperty HeightProperty;
// Register DependencyProperty in static class ctor…
static FrameworkElement()
{
…
HeightProperty = DependencyProperty.Register(
"Height",
typeof(double),
typeof(FrameworkElement),
new FrameworkPropertyMetatdata((double) 1.0 / (double) 0.0,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new ProertyChangedCallback(FrameworkElement.OnTransformDirty)),
ValidateValueCallback(FrameworkElement.IsWidthHeightVAlid));
}
// The CLR wrapper…
public double Height
{
get { return (double) base.GetValue(HeightProperty); }
set { base.SetValue(HeightProperty, value); }
}
} - Notes/comments…
GetValue()
&SetValue()
come fromDependencyObject
inheritanceDependencyProperty.Register()
has been overloaded many times- Arguments in
Height.DependencyProperty.Register()
"Height"
- name of CLR propertytypeof(double)
- type info on underlying data type to be encapsulatedtypeof(FrameworkElement)
- type info of the encapsulating class- Would seem a redundancy because
HeightProperty
already defined withinFrameworkElement
class - However, "…this is a very clever aspect of WPF in that it allows one class to register properties on another (even if the class definition has been sealed!)." (Troelsen: 5th Ed., p. 1329)
- Would seem a redundancy because
new FrameworkPropertyMetadata(…)
object- Provides info on how WPF should have this property work (if needed) with callback notifications
- Also, via
FrameworkPropertyMetadataOptions
enum specifies what is affected by the property (e.g., data binding, 'inheritability') new PropertyChangedCallback(…)
- This is a delegate
FrameworkElement.OnTransformDirty
- method to be called when value of property changes- Note 1: this is a static method
- Note 2: any time you build a custom dependency property you can employ a
PropertyChangedCallback
delegate to point to a method that will be invoked when the value of the property changes
new ValidateValueCallback(…)
object- Another delegate
- Points to another static method -
FramworkElement.IsWidthHeightValid()
- which contains predictable logic (i.e., positive, non-infinite value)
- Lastly, all effectively wrapped inside the CLR property (
public double Height
)
- CLR property wrappers
- When building a CLR property wrapper
- Never put validation logic in the
set
block - In fact, the only things CLR wrapper of a dependency wrapper should do are to call
GetValue()
SetValue()
- Never put validation logic in the
- Notes on the
get
&set
blocks- When retrieving or declaring property values in markup the runtime completely skips the
get
&set
blocks & callsSetValue()
directly! - Rationale for bypassing
get
&set
blocks- Optimization plays a big part
- If the runtime did utilize, say, the
set
block it would have to- Perform a runtime reflection to obtain location of
DependencyProperty
field - Reference that field in memory
- Perform some other tasks
- Perform a runtime reflection to obtain location of
- Why, then utilize CLR wrappers at all?
- Cannot call functions in markup
- Would have to write markup as follows, which, of course, one cannot do:
<Button x:Name="myButton" this.SetValue("100")… />
- Note: while you do not put validation code in a
set
block…- You do want to create a validation value method
- Point to that method using the
ValidateValueCallBack
delegate - This will fire whether setting property values in code or in markup
- When retrieving or declaring property values in markup the runtime completely skips the
- When building a CLR property wrapper
- General
- Building a custom dependency property (in VS)
- General
- Top-level steps
- Inside a WPF application:
- Name the control
- Resulting XAML file
- Root-level tag will be
<UserControl>
- Can set height & width of control
- Inside
<UserControl>
element place appropriate surface (e.g.,Grid
) - Inside that place the appropriate control (e.g.,
Label
)
- Root-level tag will be
- Using/testing the custom control in a WPF app
- Because your control is not part of core WFP you must create a custom namespace for it in, say, your
Window
element - Note: after adding the namespace declaration run to get XAML mark-up to recognize the namespace definition
- Syntax/example:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/etc…
xmlns:x="http//schemas.microsoft.com/etc.hellip;
xmlns:myCtrls="clr-namespace:MyApp"
other attribute declarations…>
<StackPanel>
<myCtrls:MyControl x:Name="myCtrl" MyCustomPpty="xyz" />
</StackPanel>
</Window> - Note: VS seems to add the
clr-namespace:
stub when you define that namespace
- Because your control is not part of core WFP you must create a custom namespace for it in, say, your
- Registering a custom control as a dependency property
- Modify your control's
*.xaml.cs
file - Inside the control's class scope (but not within the ctor)…
- Type
propdp
(this is a code snippet) - Click twice to get the dependency property stubbed out
MyProperty
- Change
ownerclass
entry to name of your custom control
- Type
- Notes re: the just-created skeleton code
- The code registers the property inline instead of via a static constructor (which may be fine for your purposes)
- Default value of the property is defined via a
UIPropertyMetadata
object, which is not as complex as using aFrameworkPropertyMetadata
object
- Modify your control's
- Top-level steps
- Adding a data validation routine
- Must add an argument to the
DependencyProperty.Register()
method - Argument must be of type
ValidateValueCallback
- As seen above, that delegate must point to a static method which returns a
bool
- Syntax/example:
public static readonly DependencyProperty CurrNmbrProperty =
DependencyProperty.Register("CurrNmbr", typeof(int),
typeof(MyControl),
new UIPropertyMetadata(100),
new ValidateValueCallback(ValidateCurrNmbr));
public static bool ValidateCurrNmber(object value)
{
// Business rule: value must be between 0 and 500...
if (Convert.ToInt32(value) >= 0 && Convert.ToInt32(value) <= 500)
return true;
else
return false;
}
- Must add an argument to the
- Responding to the property change
- Must add a second argument to
UIPropertyMetadata
ctor - Argument must be of type
PropertyChangedCallback
- This is another delegate
- Delegate must point to a method which takes the following two parameters (in order):
DependencyObject
DependencyPropertyChangedEventArgs
- Setting up the method which will handle the property-changed calls
- Objective, of course, is to change some property in your control to a new value
- Hurdle: the method has to be defined as static, yet you want to change the property of a specific instance of your control
- Solution: the
DependencyObject
parameter is, in fact, a reference to the instance of your control - Syntax/example:
public static void CurrentNumberChanged(
DependencyObject depObj,
DependencyPropertyChangedEventArgs args)
{
// Cast the dependency object as a ShowNumberControl…
ShowNumberControl c = (ShowNumberControl)depObj;
// Get the Label control in the ShowNumberControl…
Label theLabel = c.numberDisplay;
// Set the label with the new value…
theLabel.Content = args.NewValue.ToString();
}
- Must add a second argument to
- General
- Routed events
- General
- Issue: suppose you have a
Button
whose content includes several other elements.- How do you handle
Click
event? - Without routed events would have to write
Click
event handlers in each contained element
- How do you handle
- Routed events solution
- Can place
Click
event handler in markup for parentButton
element - Because XAML describes a tree of objects it's 'easy' for WPF to put event-handling in a parent element
- Can place
- Events in WPF work with
RoutedEventHandler
delegates- Of course, you typically name these delegates something like
myButton_Clicked()
- These delegates take two parameters (in order):
- An
object
(generally namedsender
) - A
System.Windows.RoutedEventArgs
(generally namede
) type
- An
- Of course, you typically name these delegates something like
- A routed event can in fact follow one of 3 routing strategies:
- Bubbling event - when an event move from child to parent element(s)
- Tunneling event - when an event moves from container element to contained element
- Direct event - event is handled by/within the originating element
- Issue: suppose you have a
- Routed bubbling events
- Note: routed bubbling events always move from child to immediate parent - never from child to a sibling
- If you have several sibling descendants & you want to handle any of them uniquely simply put event handler(s) in the 'unique' element(s)
- Note re:
Click
events- Assume you have a
Button
with several elements as content - Assume further:
- One element contained by the
Button
is aLabel
- You do not want the
Label
's 'Click
' event to bubble up to theButton
- i.e., you want to handle theLabel
uniquely
- One element contained by the
- Ostensibly you run into a problem here because
Click
is not a recognized event for aLabel
- Solution: handle the
MouseDown
event in theLabel
, which is a supported event
- Assume you have a
- Continuing or halting bubbling
- In scenario just described (Routed bubbling events) where you set up a special event handler for a descendent element
- That unique element will handle its own event
- However, the event will then still bubble up & fire the parent/grandparent/etc. event handler
- To prevent that bubbling, close out the unique element's event handling code with
e.Handled = true;
- In scenario just described (Routed bubbling events) where you set up a special event handler for a descendent element
- Routed tunneling events
- Tunneling events all carry
Preview
prefix (e.g.,PreviewClick
) - "By and large, each bubbling event in the WPF base class libraries is paired with a related tunneling event that fires before the bubbling counterpart." (Troelsen: 5th Ed., p. 1339)
- The rationale for pairing events in this fashion is that this gives you the ability to perform error trapping
- Example: you have a text box for a user to enter a zip code
- At the very least can trap for alpha characters
- After, say, a
MessageBox.Show(…)
would want to sete.Handled = true;
- Tunneling events all carry
- General
- Logical trees, visual trees, & default templates
- General
- Logical tree
- Can work with via either XAML or directly via C#
- Represents how content will be positioned
- Visual tree
- Includes
- Templates
- Styles
- Also includes
- Animations
- Drawings
- Shapes
- Visual tree used internally by WPF
- Visual tree controls how elements are rendered on the screen
- Includes
- WPF controls are lookless
- "This refers to the fact that the look and feel of a WPF control is completely independent (and customizable) from its behavior." (Troelsen: 5th Ed., p. 1341)
- In other words, you can completely change the look of, say, a
Button
, yet it is still aButton
- Logical tree
- Programmatically inspecting a logical tree
- Typically not done at runtime
- However, if you do want to investigate a logical tree you can use the
LogicalTreeHelper
class (inSystem.Windows
namespace) - See Troelsen: 5th Ed., pp. 1342-3, for an example
- Programmatically inspecting a visual tree
- Can use the
VisualTreeHelper
class (inSystem.Windows
namespace) - See Troelsen: 5th Ed., pp. 1343-4, for an example
- Types of elements you can encounter
ContentPresenter
AdornerDecorator
ButtonChrome
- Can use the
- Programmatically inspecting a control's default template
- All rendering commands for a WPF control are contained within that control's default template
- In turn, these templates are all instances of the
ControlTemplate
class - Can access a control's default template via the
Template
property - The
Template
property can also be used to replace a control's default template - Expression Blend is particularly well-suited for building new templates
- See Troelsen: 5th Ed., pp. 1344-8, for example/code/markup
- General
- Building a custom control template with VS 2010
- General
- Can create a new control template using code
- Typically, though, one uses either Expression Blend or VS
- Syntax/example:
<Window x:Class="ButtonTemplate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="myButton" Width="100" Height="100"
Click="myButton_Click" >
<Button.Template>
<ControlTemplate>
<Grid x:Name="controlLayout">
<Ellipse x:Name="buttonSurface" Fill="LightBlue" />
<Label x:Name="btnCaption" VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold" FontSize="20" Content="OK!" />
</Grid>
Other closing tags… - Notes…
- In
Grid
control when you do not define rows or columns each child element stacks on top of the prior one - Very important feature of changing a template like this is the treatment of
Click
event- Will not fire unless user clicks within bounds of redefined/contained elements (i.e., will not fire if you click within 'invisible' corners of the
Button
) - This saves you from having to handle things like hit-testing & bounds checking
- Will not fire unless user clicks within bounds of redefined/contained elements (i.e., will not fire if you click within 'invisible' corners of the
- In
- Templates as resources
- Drawback of above markup is that the template is only available to its container button
- Can place a custom template in either
- Resource directory (for cross-project use)
- Application resource container
- To promote a template to a resource in VS
- Select your element inside the design window
- Scroll to property inside window
- Click on small black diamond next to then from pop-up menu
- Name your template
- Note: inside just-created
<ControlTemplate>
element (presumably in<Application>
element) you may well want to restrictTargetType
, à la styles
- Incorporating visual cues using triggers
- A drawback of defining & applying a custom template is that you lose all visual cues of the default template
- A
Button
's default template, for example, slightly changes look & feel whenButton
- Has focus
- Is clicked
- Is disabled
- As users are quite used to these default behaviors you want to do something to replace them
- Adding visual cues - how WPF originally handled matters
- Syntax/example:
<Application …>
<Application.Resources>
<ControlTemplate …>
<Grid x:Name="controlLayout">
…
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="buttonSurface" Property="Fill"
Value="Blue" />
<Setter TargetName="btnCaption" Property="Foreground"
Value="Yellow" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="controlLayout"
Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.8" ScaleY="0.8" />
</Setter.Value>
closing tags… - As of .NET 4.0, however, one handles these matters via Visual State Manager (see below)
- Syntax/example:
- {TemplateBinding} markup extension
- Can run into difficulties if/when you try to override template settings within an individual element
- In the above examples:
- Setting the
Background
color of a button will have no effect, because the appropriate property for an ellipse isFill
- Even trying to override
Content
will fail- Yes, the
Label
element does have an identically-namedContent
property - However, setting the
Content
onButton
does not automatically push that value into the child element
- Yes, the
- Setting the
- {TemplateBinding} markup extension "allows you to capture property settings defined by the control using your template and use them to set values in the template itself." (Troelsen: 5th Ed., p. 1353)
- Syntax/example (modified from above…):
…
<ControlTemplate x:Key="RoundButtonTemplate"
TargetType="{x:Type Button}">
<Grid x:Name="controlLayout">
<Ellipse x:Name="buttonSurface"
Fill="{TemplateBinding Background}" />
<Label x:Name="btnCaption"
Content="{TemplateBinding Content}"
FontWeight="Bold" FontSize="20"
VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
Closing tags, etc. …
ContentPresenter
- Using
{TemplateBinding}
you can still pass in content other than plain text - For example, inside a
Button.Content
element you could define aListBox
- However, not all elements have
Content
properties (e.g.,Grid
) - Skin that cat using a
ContentPresenter
element - Syntax/example:
<ControlTemplate x:Key="RoundButtons" TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}" />
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
- Using
- Incorporating templates into styles
- After finishing with/designing a template you still have other properties of, say, target
Button
to define - To be über efficient place/move your template unto a style!
- Inside the style simply include the following element:
<Setter Property="Template">
- As immediate child element
<Setter.Value>
- Within that incorporate all of your
ControlTemplate
markup
- Inside the style simply include the following element:
- See Troelsen: 5th Ed., pp. 1354-5, for sample markup
- After finishing with/designing a template you still have other properties of, say, target
- General
- Building custom UserControls with Blend
- General
UserControl
next logical step after control template- A control template is essentially a skin - affects physical appearance
- With a custom
UserControl
- You are defining a custom class
- As such your
UserControl
can have its own properties, methods, etc.
UserControls
often contain their own styles & templates- On the other hand, all markup can simply reside within
<UserControl>
scope - Can create
*.dll
libraries containing nothing butUserControls
- Creating a
UserControl
Library project- Renaming the initial
UserControl
- Rename
MainControl.xaml
- In XAML markup
- Change value of
x:Class
attribute - Completely remove
x:Name
attribute
- Change value of
- Rename associated class in
*.xaml.cs
file- Problem: In Blend Project would not build after renaming the class & ctor
- Workaround: Reopen the project in VS & use shortcut menu to
- (Note: problem arises because you are working with a
partial
class)
- Rename
- Designing the
SpinControl
- To add images (
*.jpg, *.png
files)- Use
- No need for any further configuration of image files
- To create a control which simply contains an image
- Place image in single row & column
<Grid>
element - Set
Grid
'sStretch
attribute to"Fill"
- May well want to add a
<Border>
element toGrid
- Place image in single row & column
- To add images (
- Adding initial C# code
- To add event handlers can use tab of Blend's window
- Simply double-click in empty box next to name of event to create stubbed-out code
- Defining an animation using Blend
- Can use Blend to create a
Storyboard
for you without writing any markup - Creating raw
Storyboard
- Make sure designer window of Blend is active
- Select control under tab/editor
- Click on button ( sign)
- Give name to storyboard in dialog box
- Adding animation to code>Storyboard
- After naming the storyboard a timeline editor will appear
- Within a
storyboard
each discrete element of time is called a keyframe - To create a
<keyframe>
element- Click the yellow arrow on the timeline & drag it out to, say, 0.5 seconds
- Click on the button (right above the zero second mark)
- A red border will appear around the visual editor, telling you that you are in record mode
- To create the animation itself (inside the
keyframe
)- Navigate to
- Identify transformations
- Note: more often than not you want to check box
- Click icon/button to review animation
- Can use the above procedures to add additional animations
- Can use Blend to create a
- Programmatically starting a
Storyboard
- By default Blend will add markup so that you animation runs when your control loads
- Syntax/example:
<UserControl.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource SpinImageStoryboard}"/>
</EventTrigger>
</UserControl.Triggers> - Can find any & all triggers under
tab (use menu to find if necessary)
- You often want to delete that trigger
- Use the icon (negative sign) under tab to do so
- Syntax/example of firing a 'Spin' animation from within code:
public int Spin()
{
// Randomly put one of the images into the Image control…
Random r = new Random(DateTime.Now.Millisecond);
int randomNumber = r.Next(3);
this.imgDisplay.Source = images[randomNumber];
// Start the storyboard animation…
((Storyboard)Resources["SpinImagesStoryboard"]).Begin();
return randomNumber;
}
- Renaming the initial
- General
- Creating the Jackpot Deluxe WPF application
- General (referencing a custom control)
- In Blend use
*.dll
file & point to appropriate - Within
<Window>
element remember to add custom namespace - Syntax/example:
<Window
…
xmlns:custom="clr-namespace:MyCustomControl;assembly=MyCustomControl"
…
</Window>
- In Blend use
- Extracting a
UserControl
from a drawingGeometry
- Use this method if you need to create a control provided by a graphic artist
- Right-click on
Path
object that is the drawing- Select
- Name the control
- Blend will
- Create a new
UserControl
- Create both the
*.cs
&*.xaml
files for the control - Inside your XAML replace the
Path
object with an instance of the newly-created control - Map the control to a new XML namespace inside the
<Window>
element
- Create a new
- .NET 4.0 visual states
- General
- Introduced to WPF (originally only in Silverlight) as of .NET 4.0
- Begin by defining a group of states that a control may be in at any one time
- "Think of a visual state group as little more than a named container for related UI cues." (Troelsen: 5th Ed., p. 1366)
- Examples/typical group names:
MouseStateGroup
FocusedStateGroup
EnabledStateGroup
- Inside a state group you then define specific states
- Examples (for the mouse state of a custom
Star
control):MouseEnterStar
MouseExitStar
MouseDownStar
- You then define a
Storyboard
to correspond to each state - Two options for moving a control into a given state
- Using code
- Call static
GoToState()
method ofVisualStateManager
class - Sole required parameter: the name of the state
- Call static
- Using only markup
- Define triggers
- Requires use of
<GoToStateAction<
element
- Using code
- Defining visual states (using Blend) for a custom control
- Select the control in Blend IDE
- In
tab (available via menu)
- Click icon
- Name the group
- Using icon add a state
- Double-click on to rename
- Note: By 'default' you wind up with the code you need for, say, a
MouseExitState
state- Each state begins with a controls current settings
- On, say,
MouseExitState
that, of course, is exactly what you want
- Defining state transition timings
- Default is for transitions to occur immediately
- To change
- In Blend click on the transition you want to target
- Click on icon
- In the listed transitions represents 'current' state
- Set the desired transition time (seconds)
- Viewing the generated XAML
- Can easily wind up with a lot of generated XAML
- You have created states, but so far have not 'invoked' moving from one state to another
- Changing the visual states in code using the
VisualStateManager
class- Use tab to stub out your event handlers
- Syntax/example:
public partial class StarButton : UserControl
{
…
private void UserControl_MouseDown(object sender,
System.Windows.Input.MouseButtonEventArgs e)
// Args: Control, Target state, Use transitions…
{
VisualStateManager.GoToState(this, "MouseDownStar",true);
}
…
}
- General
- Finalizing the Jackpot Deluxe application (see Troelsen: 5th Ed., pp. 1371-5)
- General (referencing a custom control)
- Dependency Properties
- WPF & XAML
- ASP.NET
- Building ASP.NET Web Pages
- ASP.NET Web Controls, Master Pages & Themes
- ASP.NET State Management Techniques
- Bibliography
- Troelsen, Andrew. Pro C# 2010 and the .NET 4 Platform. 5th Ed. United States of America: Apress, 2010. Print.
- Troelsen, Andrew. Pro C# 2008 and the .NET 3.5 Platform. 4th Ed. United States of America: Apress, 2007. Print.
- MSDN Library
- Charlie Calvert's Community Blog (MSDN Library)
- Wikipedia
- General
- C# 6.0 & .NET 4.6 (in progress)
- General
- .NET Platform
- COM programming (i.e., pre-.NET Programming)
- General
- Emphasizes creating reusable binary code
- COM binaries often called COM servers
- No inheritance available with COM programming
- Note: can, however, declare has-a relationships
- Language-independence: COM binaries can be written using numerous languages
- Complexity of COM data type representation
- COM binaries often created via COM-aware frameworks
- Example: ATL (Active Template Library) - C++ - based
- No standardization of type system across languages & technologies that write/rely on COM binaries
- Example:
CComSTR
(ATL) !=String
(VB6) !=char*
(in C) - This makes building public methods very complicated & tricky in COM
- General
- Building blocks of the .NET platform
- General
- CLR (Common Language Runtime)
- Primary role: locate, load & manage .NET types
- Also takes care of low-level responsibilities such as memory management, security, and handling threads
- CTS (Common Type System)
- Specification that delineates all possible data types & programming constructs
- Covers how all these things interact & how they are represented in .NET metadata
- CLS (Common Language Specification)
- Not every .NET language will support every feature defined by CTS
- CLS is a subset of CTS & represents those data types & programming constructs which each .NET language must recognize & be able to work with
- CLR (Common Language Runtime)
- The base class libraries
- C# characteristics
- General
- Additional .NET-aware programming languages (see .NET Languages)
- .NET assemblies
- Overview
- General
- .NET compilers emit CIL, not platform-specific instructions
- .NET similar to Java bytecode in that it is compiled to platform-specific instructions last-minute
- Single-file & multi-file assemblies
- The Common Intermediate Language
- Benefits of CIL
- Compiling CIL to platform-specific instructions
- JIT compiler, aka Jitter
- Jitter called upon by .NET runtime has been optimized for machine's CPU & platform whenever runtime was installed
- Note: you can pre-JIT an assembly
- Use
ngen.exe
command-line tool - Can improve start-up time with graphically intensive programs
- Use
- .NET type metadata
- The assembly manifest
- General
- The .NET building blocks
- The Common Type System
- The five types
- Classes
- Interfaces
- Structures
- Enumerations
- Delegates
- Type members
- Intrinsic CTS data types
- The five types
- The Common Language Specification
- General
- CLS == minimal set of rules, standards, etc. a language must meet in order to be considered a .NET language
- Essentially a subset of CTS functionality
- CLS only applies to public members
- Ensuring CLS compliance
- Can use a .NET attribute to have compiler check for CLS compliance
- Syntax:
[assembly: System.CLSCompliant(true)]
- General
- The Common Language Runtime
- The Java runtime engine is called Java Virtual Machine (JVM)
- One runtime engine exists for all .NET-aware languages
- The binary:
mscoree.dll
- Aka, the Common Object Runtime Execution Engine
- Key base class library for runtime engine:
mscorlib.dll
- The Common Type System
- The Assembly/Namespace/Type distinction
- General
- Code libraries
- Many programming languages come with these (essentially these are utilities)
- .NET does not employ code libraries, though
- Instead, .NET employs language-neutral .NET libraries
- Organizing .NET libraries
- "A namespace is a grouping of semantically related types contained in an assembly." (Troelsen & Japikse: 7th Ed., p. 22)
- A single assembly can contain (& in the base class libraries this is typically the case) multiple namespaces
- Any one namespace in turn typically contains numerous types
- Difference between .NET & other languages is that with .NET each language accesses the same namespace(s) & the same types
- See Table 1-3, Troelsen & Japikse: 7th Ed., p. 24, for a good summary of the most significant .NET namespaces
- Code libraries
- The MS root namespace (for Windows-based services)
- Accessing a namespace programmatically
- Employ the type-level
using
keyword - Can also use a fully-qualified name
- Employ the type-level
- Referencing external assemblies
- General
ildasm.exe
- Viewing CIL code
- Viewing type metadata (
ctrl-M
) - Viewing assembly metadata (aka The Manifest)
- Overview
- The platform-independent nature of .NET
- Common Language Infrastructure (CLI)
- Relates to deploying .NET in non-Windows environments
- CLI, along with C# language specifications, have been released to ECMA (specifications 334 335)
- CLI is the much larger specification & has 6 partitions
- CLI defines minimal set of namespaces, functionalities, etc.
- Two major implementations of CLI apart from Microsoft's .NET platform for Windows
- Mono project
- For Linux, Max OS X, iOS (iPad, iPhone), & Android devices
- Includes
- Command-line tools
- All relevant code libraries
- Xamarin, & Xamarin Studio, falls under Mono project
- .NET Core 5
- Introduced in 2014
- Open-source version of MS's full-scale, Windows-specific .NET 4.6 framework
- However…
- Not identical to 'native' .NET 4.6 framework
- Essentially a sub-set of .NET 4.6 framework
- Lacks implementations for desktop GUI APIs (e.g., WinForms & WPF)
- Use Mono for cross-platform desktop GUIs
- Used for building
- Cross-platform code libraries
- ASP.NET web apps to run on Linux & Mac OS X (and on Windows) - i.e., enterprise web apps
- Resources
- Great introductory MSDN blog about .NET Core, including a comparison of .NET Core vs. full .NET
- Visual Studio Code
- Free, light-weight code editor
- Use for cross-platform development
- Mono project
- Common Language Infrastructure (CLI)
- COM programming (i.e., pre-.NET Programming)
- Building C# Applications
- Building C# applications using csc.exe
- General
- When using command-line commands prefix options with either a dash (-) or a slash (/)
- You can compile C# applications from code written in NotePad
- Specifying input & output targets
- Use
/out
argument to specify name of assembly - Use various
/target
arguments to control what is being created - Syntax:
- At a minimum must specify the
*.cs
file you are compiling - Options precede the name of the file
- At a minimum must specify the
- Use
- Referencing external assemblies
mscorlib.dll
is automatically referenced during compilation process (i.e.,System
namespace)- Use
/reference:
(or/r:
) command-line flag for this - See The default response file re: automatically-referenced assemblies
- Referencing multiple external assemblies (use semi-colon delimited list)
- Compiling multiple source files
- Use space-delimited list
- Can also use the wildcard character (
*
) - e.g.,csc *.cs
- C# response files
- General
- Use to contain/collect various input options
- Take a
*.rsp
file extension - Denote comments with the
#
character - On command line precede the response file name with the
@
sign - Multiple
*.rsp
files- This is eminently do-able
- Use space-delimited list on command line
- Command-line options in later
*.rsp
files will override those in earlier such files
- Combining command-line options with
*.rsp
files- Can be done
- Whatever comes later overrides what went earlier
- Exception: the effect of the
/reference
flag is cumulative
- The default response file (csc.rsp)
- Located in same directory as
csc.exe
file itself - This is automatically referenced, even when supplying a custom
*.rsp
file - References numerous, common .NET assemblies via
/r:
flag /noconfig
flag disables reference tocsc.rsp
- Located in same directory as
- General
- General
- Building C# apps for Windows
- Visual Studio Express family of IDEs
- Express for Windows Desktop
- New Project dialog box & C# Code Editor
- Running & debugging a project
- Solution Explorer (under
)
- Shows external assemblies being referenced
- Use to view project properties
- Note: because every project must reference
mscorlib.dll
Solution Explorer does not even bother to list the file an external reference
- Object Browser (under )
- Referencing additional assemblies
- Viewing project properties
- Express for Web
- Express for Windows 10
- Team Foundation Server 2015 Express
- Express for Windows Desktop
- Visual Studio Community Edition IDE
- Integrated support for code refactoring
- Code expansions & surround with technology
- Snippets (inserts prefab code at cursor)
- Can insert via short-cut menu within code
- Can also insert by beginning to type relevant code and then selecting
Tab
twice - Example:
cw
is the code snippet forConsole.WriteLine();
- To see all code snippets R-click in a C# code file & select
- Surround With technology (wraps code within given scope)
- Code snippets are saved in/as XML - ergo, you can author your own
- Snippets (inserts prefab code at cursor)
- Visual Class Designer
- In R-click on your project (not on the solution) to view
- You create a class diagram file (
*.cd
) as part of your project when doing this - Drag & drop onto designer window
*.cs
files from Solution Explorer onto designer window- Types, inheritance, & comments from the Class Designer Toolbox ( )
- Class Details window
- Select
- This window is opened automatically when you create a new class designer
- Visual Studio Professional Edition IDE
- .NET Framework documentation
- Visual Studio Express family of IDEs
- Building C# apps beyond the Windows OS: Xamarin Studio
- Building C# applications using csc.exe
- .NET Platform
- Basic C# Programming Constructs
- Core Constructs - Part I
- Anatomy of a C# program
- General
- "C# demands that all program logic be contained within a type definition…" (Troelsen & Japikse: 7th Ed., p. 61)
- Ergo, global functions & global variables are not allowed
- The class that defines the
Main()
is called the application object - Multiple application objects
- They can be employed
- Useful for performing unit tests
- However, must either:
- Employ the
/main
option if utilizing the command-line compiler - In VS set the ( )
- Employ the
Main()
method- Must, of course, be declared as
static
- The
(string[] args)
parameter is the mechanism for accepting command-line arguments
- Must, of course, be declared as
- Variations on the
Main()
method- Can have
Main()
generate a return value (typically abool
orint
) - Can omit the
(string[] args)
parameter - Can define
Main()
method aspublic
(implicit/default modifier isprivate
)
- Can have
- Specifying an application error code
- Generally you have
Main()
returnvoid
- Conventions from C-based languages when returning a value
- Value of
0
indicates successful execution - Any other value (though typically
-1
) indicates an error - Note: even when return type is explicitly set to
void Main()
in fact returns a0
upon successful completion
- Value of
- Application error codes in Windows
- Application return value stored in
%ERRORLEVEL%
environment variable - Can obtain this value via
System.Diagnostics.Process.ExitCode
property - Note: Cannot get this error code in-process; only available after the application has crashed & burned
- Need to include a
return
statement to control the error code thrown off byMain()
method - See Troelsen & Japikse: 7th Ed., pp. 64‑5, for an example of invoking a CS program from within a DOS/batch file & then handling the return value
- Application return value stored in
- Generally you have
- Processing command-line arguments
- Can use
for
orforeach
commands - Can also use
GetCommandLineArgs()
method of theSystem.Environment
type- First index returned by the method is the name of the application itself
- Remaining items in returned array == the passed-in parameters
- Note: When using this approach including the
string[] args
as an input parameter forMain()
becomes superfluous (though not at all harmful)
- Can use
- Specifying command-line arguments with VS
- Fill in box under
- Delimit multiple input parameters with spaces
- General
- Two base classes of note
System.Environment
classSystem.Console
class- Basic I/O with the
Console
class (see Troelsen & Japikse: 7th Ed., pp. 69‑71) - Formatting Console output
- Use tokens, such as {0}, as placeholders for "variables"
- Always begin with 0
- A token can be used more than once in a string
- Tokens need not be used in order
- Formatting numerical data
- Can include various formatting characters, aka format tags
- Syntax (to generate "I have $99.00"):
Console.WriteLine("I have {0:c}",99);
- With some formatting characters you also include a digit to specify the number of decimals, etc.
- .NET includes a wide variety of other formatting types you can employ
- Formatting numerical data beyond Console applications
- Can use string formatting characters inside the
string.Format()
method - Example:
string myNmbr = string.Format("I have {0:c}",99);
- Can use string formatting characters inside the
- Basic I/O with the
- Data types
- System data types & C# shorthand notation
- General
- Note: not all C# data-types are CLS-compliant
- See Troelsen & Japikse: 7th Ed., Table 3-4, p. 74, for a full list of data types
- Summary
- Whole numbers
sbyte; byte:
-128 to +127; 0 to 255short; ushort:
-32K to +32K; 0 to 65Kint; uint:
-2.1B to +2.1B; 0 to 4.3Blong; ulong:
-9 Quint to +9 Quint; 0 to 18 Quint
- Decimal numbers - in increasing order of precision & size
float
double
decimal
- Whole numbers
- Two defaults to be aware of
- Floating point numbers
- Treated as doubles by default
- To override that attach an
F
or anf
to the raw numerical value - e.g.,5.3F
- Raw whole numbers
- Default to
int
data types - Attach
L
orl
(e.g.,10L
) to force to along
type
- Default to
- Floating point numbers
- Numerical types map to structures in the
System
namespace - Structures are value types which are allocated on the stack
- Variable declaration & initialization
- You can…
- Declare & assign initial values in one statement
- Declare multiple variables of the same type on one line (delimit declarations with a comma)
- Declare any data type using its full
System.
name
- Assigning initial values
- Your compiler will barf if you attempt to use a variable to which you have not assigned a value
- Best Practice: assign an initial value (or 'new' it) whenever you declare a variable
- You can…
- Intrinsic data types & the
new
operator- All data types have a default constructor
- Declaring a data type with the
new
keyword then assigns a default value to the variable - Syntax (2 examples):
bool b = new bool();
int i = new int(); - Note: 'new-ing' variables like this is rather cumbersome
- The data type class hierarchy
- Every type (of course) derives from
System.Object
- Ergo, every data type has methods such as
ToString(), Equals
, etc. - Numerical data types derive from
System.Value.Type
- Every type (of course) derives from
- Intrinsic data types
- Numerical data types
- All numeric types support
MaxValue
&MinValue
properties - Certain data types have other members: e.g.,
System.Double
supports methods for positive & negative infinity
- All numeric types support
System.Boolean
(supportsTrueString
&FalseString
properties)System.Char
- Both
char
&string
types are Unicode - Remarkable number of static methods available for the
System.Char
type
- Both
- Parsing values from
String
data- Use the static
Parse()
method of the underlying data type - Example:
bool b = bool.Parse("True");
- Use the static
System.DateTime
&System.TimeSpan
- These are among the few data types for which there is no C# keyword
- You do have to
new
these data types System.TimeSpan
in particular has a number of interesting members
- The .NET 4.0
System.Numerics
namespace- New namespace in .NET 4.0
- Defines a structure called
BigInteger
- (Namespace also defines a structure called
Complex
, which is designed to be used in mathematical modeling) - To use:
- Add a reference to
System.Numerics.dll
to your project - Add the appropriate
using
statement
- Add a reference to
- Note:
BigInteger
values are immutable - See Troelsen & Japikse: 7th Ed., pp. 82‑3, on how to create and manipulate
BigInteger
values
- Numerical data types
- General
- Working with string data
- Basic string manipulation
System.String
is essentially a utility class- Many methods can be called on a string variable itself
- Example:
int i = "Bill".Length;
- However, some methods are static
- String concatenation
- In C# use the
+
symbol (or+=
) - CLR actually converts this symbol to a call to the
String.Concat()
method when compiling the CIL
- In C# use the
- Escape characters
- Escape characters begin with a backslash (i.e., "\")
- See Troelsen & Japikse: 7th Ed. Table 3-6, p. 86, for a listing
- Defining verbatim strings
- Apply the WYSWYG principle to strings
- Preface the string with the @ sign (it comes before the opening quotation mark defining the string)
- Verbatim strings preserve white space
- To preserve quotation marks inside a verbatim use two consecutive quotation marks.
- Strings & equality
- The normal approach with reference types (which is what strings are) is to determine equality by seeing if the references are pointing to the same object in memory
- With strings, though, that approach has been overridden; equality is based on the values of the string types
- In C# equality of strings is case-sensitive
- The immutability of strings
- Once declared the character data of string objects cannot be changed
- If & when you run string manipulation functions you are actually creating new
string
objects - If you assign a new value to a
string
variable the initial value remains on the heap until garbage collected - Significance of the immutability of
string
objects is that in a project with a lot of string concatenation & manipulation & such you can easily eat up a lot of memory
- The
System.Text.StringBuilder
type- This is the object of choice when faced with a lot of string manipulation
- Resides in the
System.Text
namespace - Must
new
a variable of theStringBuilder
class - Especially useful for creating somewhat longer or multi-line strings
- Build your text using
StringBuilder
'sAppendLine()
method - When done use the
ToString()
method
- Build your text using
- Unlike working with 'normal' string manipulation, when working with
StringBuilder
- You are, in fact, changing the given string object
- This obviously means you are not 'leaving around' a bunch of extraneous string objects as you modify your text
StringBuilder
initially holds 16 chars or fewer- You can change that initial buffer size when new-ing the
StringBuilder
instance StringBuilder
does automatically expand its buffer size as needed
- You can change that initial buffer size when new-ing the
- String interpolation
- New feature as of .NET 4.6
- Alternate method for building strings from hard-coded text & variables
- Indicated by prefixing string construction with a dollar sign - i.e., $
- Syntax/example:
static void StringInterpolation()
{
// Local variables…
int ageWks = 11;
string name = "Brendan Colin";
//Using string interpolation…
string greeting = $"My grandson {name} is {ageWks} weeks old.";
string greeting2 = $"My grandson {name.ToUpper()} is {ageWks} weeks old.";
// Do something with the generated string variables… } - Note 1: The curly brackets operate as valid scope brackets
- Hence the ability to use, say,
{name.ToUpper()}
- Can also include a statement like
{age += 1}
- However…
- You do not include any semi-colons within these brackets
- This, in turn, means that you cannot include multi-line statements
- Hence the ability to use, say,
- You can also continue to include escape characters, such as
\n
- Note 2: use the traditional numerical placeholder approach if there is any chance you will need to compile against prior versions of .NET, as string interpolation is only recognized as of .NET 4.6 framework
- Basic string manipulation
- Narrowing & widening data type conversions
- General
- Data type mismatches are never a problem when there is no possibility of data loss
- Example: You can pass a
short
as a parameter to a method which is expecting anint
- In this example the compiler automatically widens the
short
to anint
- Implicit widening is more formally referred to as an upward cast (again, there is no possibility of data loss).
- Implicit narrowing operations, on the other hand, always generate compiler errors, even when the values assigned to the variable would fit/work.
- When employing a narrowing operation you must make use of an explicit cast
- Syntax/Example:
byte myByte = 0;
int myInt = 200;
myByte = (byte)myInt;
- Trapping narrowing data conversions
- Obviously, you always run the risk of data loss with narrowing casts
- Use
checked & unchecked
keywords to test for data overflow conditions (e.g., abyte
value greater than 255). checked
keyword tells system to throw aSystem.OverflowException
when a value does not fit into the range of the underlying data type- At that point you can then program within a
try/catch
block (ignored in the next 2 examples) - Syntax 1:
byte myByte = 0;
int myInt = 200;
myByte = checked((byte)myInt); - Syntax 2:
checked
{
byte myByte = 0;
int myInt = 200;
myByte = (byte)myInt;
// catch for OverflowException…
}
- Setting project-wide Overflow Checking
- Do not continually have to wrap your statements in a
checked
block - Can set the
/checked
flag in the command-line compiler - In VS click
- Employing this setting is especially useful when building and debugging code
- Disabling the setting, however, will increase the runtime performance of production release
- Do not continually have to wrap your statements in a
- The
unchecked
keyword- For obvious reasons it's rarely used
- When used it typically overrides the
checked
keyword in a limited portion of code
System.Convert
- Can either widen or narrow
- Syntax/Example:
byte myByte = 0;
int myInt = 200;
myByte = Convert.ToByte(myInt); - One benefit of this method is that it is language neutral (e.g., VB has a different syntax for casting)
- General
- Implicitly typed local variables
- General
- Use
var
"data type" to have compiler infer the variable's data type at runtime - Note:
var
is not technically a keyword- You can use "var" as a name for
parameters, variables & fields
- (Doing so, however, will lead to tears.)
- When
var
is used to specify a data type when declaring a variable the compiler recognizesvar
as a keyword - To be precise,
var
is a contextual token
- You can use "var" as a name for
- Can use implicit typing for
arrays, generics, custom types
- for any data type at all
- Use
- Restrictions on implicitly typed local variables
- Can use
var
keyword only with local variables in method or property scope - Cannot use
var
in the following:- Return values
- To be more specific, you cannot define a method which returns a
var
- However, so long as there is a match in the underlying data types you can use an already-defined implicitly typed local variable following the
return
keyword within a method - See Troelsen & Japikse: 7th Ed., pp. 98‑9, for examples & discussion
- To be more specific, you cannot define a method which returns a
- Parameters
- Field data inside a custom type
- Return values
- Must be assigned an initial value at the time of declaration
- Implicitly typed local variables &
null
- Cannot assign a value of
null
when declaring an implicitly typed local variable - However, once you allocate & configure the variable you can subsequently change the its value to
null
(assuming, of course, that the data type is a reference type)
- Cannot assign a value of
- Using implicitly typed local variables in assignment statements
- You can use another variable to assign the initial value to an implicitly typed local variable
- You can conversely use an implicitly typed local variable to assign an initial value to another variable
- Note: when doing the latter the variable whose value you are assigning can itself be an implicitly typed local variable
- Cannot use the C#
?
token with thevar
keyword - i.e., cannot declare asnullable
- Can use
- Implicitly typed data is strongly typed data
- At runtime an implicitly typed local variable still becomes strongly typed as soon as it is allocated & configured
- Note: this is different from using the
var
keyword in, say, JavaScript - Therefore, as with "regular" variables you will receive a compile-time error if you pass in a value that is of a different data type
- Usefulness of implicitly typed local variables
- Prima facie, the use of the
var
keyword seems like spectacularly bad programming practice in a strongly-typed programming language - Where the use of implicitly typed local variables is essential, however (and, about the only place where you want to use the
var
keyword) is to receive values with LINQ query expressions
- Prima facie, the use of the
- General
- System data types & C# shorthand notation
- Looping & branching
- C# iteration constructs
- The
for
loop- Syntax/Example:
for(int i =0;i < 4; i++)
{ /* code to iterate over… */ } - Note: the variable
i
is only visible within the iteration code - To loop in reverse use
i--
- Syntax/Example:
- The
foreach
loop- General
- Used to traverse through all members of an array or a collection
- Syntax/Example:
string[] beatles={"John", "Paul", "George", "Ringo"};
foreach(string b in beatles)
Console.WriteLine(b); - Note: when iterating over a collection the
foreach
keyword is used in conjunction with theIEnumerator & IEnumerable
interfaces - Limitations on
foreach
loops- Can only go forward
- Cannot skip items (e.g., cannot do every 3rd item)
- The use of
var
withinforeach
constructs- Can definitely be done
- In practice, you only - but often must - do this in conjunction with LINQ
- General
- The
while
&do/while
looping constructs- No guarantee that
while
loop will execute even once (condition may not be met) - By contrast,
do/while
loop will execute at least once - See Troelsen & Japikse: 7th Ed., p. 103, for syntax & sample code
- No guarantee that
- Keywords available within
for
loop (& other iterations):goto
- Directs code to a labeled part of code
- Just as with VBA, a label is a word followed by a colon (e.g.,
SkipToHere:
) - Note: this means you can also skip to
Case:
statements within aswitch
construct
continue
- jumps to the next iteration of your loopbreak
- breaks out of the loop
- The
- Decision constructs & the relational/equality operators
if/else
statements- Some languages allow their
if/else
statements to proceed based on whether a condition evaluates to, say, 0 or -1 - In C#
if/else
operates based solely on boolean expressions
- Some languages allow their
- Equality & relational operators
- You test for equality in C# with
==
, not with=
, which is used for assignment - Negation is indicated with
!
- You test for equality in C# with
- Conditional operators
- AND & OR operators are
&&
and¦¦
- Note:
- These two operators will stop evaluating as soon as they reach
false
- If you need an entire condition evaluated use
&
and¦brvbar;
- These two operators will stop evaluating as soon as they reach
- AND & OR operators are
- The
switch
statement- Must include
break
keyword at the end of each section ofcase
code - This holds true even for the
default
statement switch
statement can operate on string as well as numeric results- See Troelsen & Japikse: 7th Ed., pp. 105‑7, for syntax & sample code
- Must include
- C# iteration constructs
- Anatomy of a C# program
- Core Constructs - Part II
- Method & parameter modifiers
- General
- Parameter modifiers precede data type keywords when specifying method parameters
- If a parameter is not marked with a modifier an argument is assumed to be passed by value
- Default parameter-passing behavior
- This means a copy of the data is given to the method
- Note: how that copy is handled will differ based on whether the passed argument is a value or reference type
- Modifiers
- The
out
modifier- A method must assign a value to every output parameter (i.e., those marked with
out
keyword) - Failure to do so generates a compile error
- Within the method do not use
return
keyword to assign value to output parameter; simply assign a value - Calling methods:
- Must also make use of the
out
keyword when specifying arguments - Do not need to assign values to a variable before passing it in as an
out
argument (since, after all, the called method must provide a value)
- Must also make use of the
- You can declare multiple output parameters within a single method
- A method must assign a value to every output parameter (i.e., those marked with
- The
ref
modifier- Use when you want a method to change the value of a data point
- You must initialize a variable before passing it in as by reference (unlike the case with an
out
parameter)
- The
params
modifier- C# supports the use of parameter arrays
- This "allows you to pass into a method a variable number of identically typed values as a single logical parameter." (Troelsen & Japikse: 7th Ed., p. 114)
- Syntax/example:
static int SumValues(params int[] value)
{
//Code to sum values…
} - When a method takes a
params
parameter you can also can also send in a strongly-typed array of comma-delimited items- This latter approach tends to be more efficient than the former approach
- When you do this C# silently creates the array for you
- With the former approach you have to:
- Declare an array variable
- Populate that variable
- Pass the variable into the method
- Methods can only take a single
params
argument - A
params
argument must be the final argument
- The
- Defining optional parameters
- New feature as of .NET 4.0
- Feature is added primarily to provide easier compatibility with COM objects
- There is no
optional
keyword - Rather, to make a parameter optional you provide an initial/default value directly in the parameter-specification section of your code
- Further, that default value must be known at compile time
- Therefore, the following method signature will not compile:
static void EnterLogData(string message,
DateTime timestamp = DateTime.Now) - Optional parameters must always appear at the end of a method's signature
- Invoking methods using named parameters
- Also new as of .NET 4.0
- Also added to enhance compatibility with COM objects
- Allows you to invoke arguments in any order you choose
- Use
:
rather than VBA's:=
to specify a named parameter - If you mix named & unnamed parameters you must list all of your unnamed parameters first
- Using named parameters can be very helpful when dealing with a method with multiple optional arguments
- Method overloading
- Methods must differ by type and/or number of parameters
- Methods which differ only by return type cannot be distinguished by overloading
- Do not use
overloads
keyword to overload a method - Simply declare additional versions of the method
- General
- Advanced .NET "data types"
- Arrays
- General
- Elements in an array must be of the same type
- Note: because arrays are zero-based upper bound will be 1 less than the declared number of members
- If you declare an array & do not specify a value for a given index that item will be set to the default value of the data type
- Can use arrays as arguments or as return values
- C# array initialization syntax (4 sample approaches)
- Syntax 1 (using the
new
keyword & size):
string[] arrFabFour = new string[4]
{ "John", "Paul", "George", "Ringo" }; - Syntax 2 (using the
new
keyword, no size):
string[] arrMarxBros = new string[]
{ "Harpo", "Groucho", "Chico" }; - Syntax 3 (with neither size nor the
new
keyword):
string[] arrStooges = { "Mo", "Larry", "Curly" };
- Syntax 4 (using the
Array
keyword):
Array arrEnums = Enum.GetValues(someEnumVar.GetType());
- Notes:
- You can omit declaring the size of an array whenever you use curly-bracket syntax (the compiler figures out the size based on the number of elements you declare)
- Use of the
new
keyword is optional
- Syntax 1 (using the
- Implicitly typed local arrays
- Can use the
var
"data type" when initializing arrays - Items within array must still be of the same underlying data type
- The
new
keyword- Must use
- However, you do not specify a data type
- Syntax/Example:
var[] a = new[] { 1, 3, 5, 7 };
- Can use the
- Defining an array of Objects
- It can be done
- Effectively allows you to skirt the requirement that all items in an array be of the same type
- Multidimensional arrays
- Rectangular array
- Simply an array of multiple dimensions
- Syntax/Example:
int[,] my2DArray;
my2DArray = new int[6,6];
- Jagged array
- This is an array of arrays
- Syntax/Example:
//An array of 5 inner arrays…
int[][] myJagArray = new int[5][];
- Rectangular array
- The
System.Array
base class- Methods
Clear()
- static- Can specify starting point & number of elements to clear
- Items cleared are set to their empty/default values
CopyTo()
- staticReverse()
- staticSort()
- Works only on 1-dimensional arrays
- Works on all intrinsic types
- Will work on custom types which implement the
IComparer
interface
- Others
- Properties
Length
(number of items in the array)Rank
(number of dimensions in the array)- Others
- Methods
- General
- Enums
- General
- If you do not declare specific values enums are assigned sequential values of 0, 1, 2, etc.
- Syntax: When specifying members in an
enum
the list should be comma-delimited
- Controlling the underlying storage for an
Enum
- Default data type used to store
enum
values isSystem.Int32
- Syntax/Example - for overriding underlying data type:
enum BeatleMemb : byte
{
John = 50,
Paul = 200,…
} - This can be useful when programming for a low-memory device like a phone
- Notes on specifying numerical values for your enumerated items
- If (above) you did not specify any numerical values for the Beatles other than
John = 50
then Paul would have a value of 51, George 52, & Ringo 53; - Assigned numerical values do not have to be in ascending order
- If (above) you did not specify any numerical values for the Beatles other than
- Default data type used to store
- Declaring
Enum
variables - The
System.Enum
type- Custom enumerations derive from the
System.Enum
class type - Will frequently use the static
GetUnderlyingType()
static method ofSytem.Enum
- This method does not take an enumeration as a parameter
- Instead, this method expects to be passed a
System.Type
- Two options for generating this
System.Type
parameter:- If you want to use a variable of your enumeration type take advantage of
GetType()
, which, of course, is "always" available, as it is a method ofSystem.Object
:
Enum.GetUnderlyingType(variableOfYourEnumType.GetType())
- If you want to use a reference to your enumeration type itself, simply use the
typeof()
method:
Enum.GetUnderlyingType(typeof(yourEnum))
- If you want to use a variable of your enumeration type take advantage of
- Custom enumerations derive from the
- Discovering an
Enum
's name/value pairs- For enumerations
ToString()
has been defined to give you the name of the enumeration value - Can cast an enumerated variable against the underlying data type (e.g.,
(byte)beatle
) to get the underlying value - Can now easily construct a
for
loop to give you the information - Note: there are also two other useful (static) methods
Enum.Format()
Enum.GetValues()
- This returns a 1-dimensional
System.Array
- See Troelsen & Japikse: 7th Ed., pp. 131‑3, for sample code using this method
- This returns a 1-dimensional
- For enumerations
- General
- Structures
- General
- More than just a user-defined type for creating a collection of name/value pairs
- Can contain data fields
- Can contain methods which operate on those fields
- Cannot inherit from structures, though
- Creating structure variables
- If you do not
new
a structure when creating it you must assign values to field data before invoking any method which utilizes the field(s) - On the other hand, if you do new a structure the default constructor will assign default values to all fields
- You can create custom constructors
- However, custom constructors must take arguments
- Unlike the case with classes, creating custom constructors for structures does not erase the default constructor!
- Custom constructors & automatic property syntax
- You encounter some esoteric considerations when you build a structure which employs both:
- Custom constructors
- Automatic property syntax (e.g.,
public int SomePpty { get; set; }
)
- Reason:
- When working with automatic properties there are no private fields (at least in your code) for which you can set to their default values
- Those private backing fields do exist in the CIL, however
- Further, the only way to set those private backing fields to their default values is by invoking the default constructor
- Thus, whenever you use automatic property syntax & custom constructors in a structure be sure to chain your custom constructor(s) to the default constructor
- Syntax/example:
public struct Rectangle
{
// Automatic ppts…
public int Width { get; set; }
public int Height { get; set; }
// Again, custom ctors in structs do NOT erase default ctor…
public Rectangle(int w, int h) : this() // …chain to default ctor
{
Width = w; Height = h;
}
}
- You encounter some esoteric considerations when you build a structure which employs both:
- If you do not
- General
- Value types & reference types
- General
- Structures have no identically-named entity in the .NET library - i.e., there is no
System.Structure
type - Structures derive (implicitly) from
System.ValueType
- As a value type, all structures are allocated on the stack rather than on the (garbage-collected) heap
- Items on the stack are removed from memory as soon as they go out of scope
- A closer look at
System.ValueType
- Essentially exists only to override the virtual methods of
System.Object
- Purpose of overriding these methods is to create value-based implementations (
System.Object
methods are reference-based) - Instance methods of
System.ValueType
are identical to those ofSystem.Object
:
public abstract class ValueType : object
{
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual string ToString();
}
- Essentially exists only to override the virtual methods of
- All numerical data types, plus enumerations & custom structures, are value types
- Structures have no identically-named entity in the .NET library - i.e., there is no
- Value types, reference types, & the assignment operator
- "When you assign one value type to another, a member-by-member copy of the field data is achieved." (Troelsen & Japikse: 7th Ed., p. 137)
- When working with numeric data types you are simply copying the value
- When working with structures the values of all fields are copied to the new structure
- Example:
int i = 10;
int j = i;
i=15;
// … j is still == 10
- Post-assignment behavior of reference types (i.e., all classes) is very different, though
- The assignment operator changes or redirects what a reference variable points to in memory
- In the above example, if
ints
were classes, thenj
would also be equal to 15 after changing the value ofi
to 15 - This is because setting
j = i
simply means "havej
point to the same object on the heap to whichi
is pointing"
- "When you assign one value type to another, a member-by-member copy of the field data is achieved." (Troelsen & Japikse: 7th Ed., p. 137)
- Value types containing reference types
- You can & might create a structure where an internal field is of a reference data type
- Assume you do just that & and set
struct2 = struct1
- Changes in any of the
value
fields of 1 struct will still not affect the value of that field in the other struct - However, a change in any property of the embedded reference-type field in one struct will be reflected in the other struct
- When you set
struct2 = struct1
any embedded reference field then contains a pointer to the same item in memory - This is also known as creating a shallow copy
- Changes in any of the
- Note: To create a deep copy you need to implement the
ICloneable
interface when creating your struct or value type
- Passing reference types by value
- This situation tends to arise when you have to supply a reference type as a parameter to a method
- Reminder: when creating a method if no appropriate keyword is provided parameters are passed by value!
- What gets passed is a copy of the reference to the caller's object
- Ergo, the method receiving the passed-in parameter can then invoke methods & change properties of that object (i.e., calling object will see the results of those changes)
- What the receiving method cannot do, however, is…
- To cause the caller's reference to point to a completely new or different object
- Note: you would accomplish that (or attempt to) by re-newing the passed-in parameter, so that it would (otherwise) point to a whole new object
- Passing reference types by reference
- Called method can still, of course, change the object's state
- In addition, called method can also change the item to which the original variable is pointing to
- See Troelsen & Japikse: 7th Ed., Table 4-3, pp. 143‑4, for a good summary of differences between value & reference types
- General
- C# nullable types
- General
- A value of
null
indicates an empty object reference - Ergo, value types can never take the value of
null
- Note: as reference types, strings can take a
null
value - Since .NET 2.0 you have, however, had the ability to create nullable data types
- Typically most convenient when working with database values
- You create a nullable type by appending a question mark (
?
) to the end of the data type- You can only use this syntax with value types (it's obviously redundant to do so with reference types)
- Local nullable variables must still be assigned a value before they can be used
- This syntax is shorthand for creating an instance of the generic
System.Nullable<T>
structure type - i.e.,Nullable<bool>
is synonymous withbool?
- Note: This inheritance exposes some members not available to regular value types
- A value of
- Working with nullable types
- You can declare a nullable type as the return value of a method
- If you define, say, variable
i
to be a nullable int- You can see whether or not it
== null
withi.HasValue
boolean property - Assuming that the variable does have a value you can then utilize the
i.Value
property
- You can see whether or not it
- The
??
operator- Sometimes you want to assign a "default" value that will "override" assigning a value of
null
- Could of course construct an
if
statement around the variable'sHasValue
property - Shortcut syntax:
SomeDbReaderClass dr = new SomeDbReaderClass();
//Assume class has a method for returning a nullable int…
int myInt = dr.GetSomeNullableIntValue() ?? 100; - The proper term for the
??
operator is the null coalescing operator
- Sometimes you want to assign a "default" value that will "override" assigning a value of
- The null conditional operator
- Very common in code to want to check incoming parameters, return values, others to see if they are a
null
value - You typically checked that with an
if
statement - As of C# 6.0 there is now a null conditional operator
- Syntax/example to avoid throwing an error if the incoming argument is null…
static void HandlingANullableParameterMethod(string[] args)
{
// Run-time engine will skip the following if args is null…
Console.WriteLine($"Method received {args?.Length} arguments.");
} - Syntax/example to generate something when an the incoming argument is null…
static void HandlingANullableParameterMethod(string[] args)
{
// Use null coalescing operator to hand null array data…
Console.WriteLine($"Method received {args?.Length ?? 0} arguments.");
}
- Very common in code to want to check incoming parameters, return values, others to see if they are a
- General
- Other
- Random
- Syntax:
random r = new Random();
- Random types have a number of methods:
Next()
- Overloaded
- Returns an
int
NextDouble()
- Syntax:
- Random
- Arrays
- Arithmetic shortcut notations
- Increment/decrement
- Post-increment/-decrement
- This uses the 'original' value of a variable in an expression, & then changes the value by 1:
- To increment a variable by 1:
i++;
- To decrement a variable by 1:
i--;
- Pre-increment/-decrement
- This first changes the value of a variable & then uses that changed value in an expression
- To increment a variable by 1:
++i;
- To decrement a variable by 1:
--i;
- Post-increment/-decrement
- To increase the value of one variable by the amount of another:
x += y;
- To decrease the value of one variable by the amount of another:
x -= y;
- Increment/decrement
- Method & parameter modifiers
- Encapsulated Class Types
- The C# class type
- General
- Field data comprise the state of a class
- Classes, of course, also contain members (including constructors) that operate on these data
- Note: You typically create an individual
*.cs
file for each class in your code
- Allocating objects with the
new
keyword- Allocating (i.e., new-ing) an object is what, in fact, creates the object in memory
- Declaring a variable of your object type only creates a pointer that refers to the object in memory
- General
- Creating classes - basic
- Constructors
- General
- Used to assign values to an object's internal fields
- Constructors never have a return value - not even
void
- Constructors always take the name of the class they are identifying
- The default constructor
- Always exists (invisibly) if you do not create another constructor
- Sets fields to their default data type values
- Can be redefined
- Defining custom constructors
- Can define multiple constructors for a given class
- As always when overloading a method, these must differ by type or number of arguments
- The default constructor - revisited
- Default constructor is silently removed as soon as you create a custom constructor
- If you then still want the default constructor you must explicitly redefine it
- There is a code snippet for defining a default constructor
- General
- The
this
keyword- General
- Gives you access to the current class instance
- Use to resolve scope ambiguity where you've been foolish enough to have a method parameter with the same name as a class field
- Tip: In a big class where you may have forgotten a method or field name, typing causes intellisense to display all class members for you
static
class members- You will get a compile error if you attempt to use
this
keyword inside a static member - That is because
static
members are those which only operate at the class level, & thethis
keyword pertains to an instance of the class
- You will get a compile error if you attempt to use
- Chaining constructor calls using
this
- Constructor chaining is a very useful technique when you have multiple constructors
- Very often constructors perform some validation on incoming parameters
- Without constructor chaining you would have to repeat that validation code inside each constructor
- You could pull the validation code into its own method & have each constructor call that validation method, but even that is ultimately redundant
- Optimal approach:
- Put all validation code inside the constructor that takes the greatest number of arguments
- Have additional constructors call the master constructor by use of the
this
keyword - These other constructors then pass, say, default values for parameters where appropriate
- Syntax (Car example):
//1 constructor…
public Car(string name) : this(name,0) {}
//Master constructor…
public Car(string name, int speed)
{
//validation - e.g., if speed > 80… - etc.
}
- Constructor flow
- When chaining constructors be aware that there is a call stack
- A called constructor has control returned to it after it has invoked a master constructor
- Therefore, the called constructor can still execute code after invoking a master constructor
- Optional arguments - revisited
- As of .NET 4.0 constructors get easier still
- One can define a single constructor & make any & all input parameters optional
- Note: you would want to set all parameters to their default values
- Especially useful when used in conjunction with named arguments
- General
- The
static
keyword- General
- Static members must be invoked on the class itself, not on an object reference
- "Simply put, static members are items that are deemed (by the class designer) to be so commonplace that there is no need to create an instance of the type when invoking the member." (Troelsen & Japikse: 7th Ed., p. 166)
- Static methods are most typically found in "utility" classes
- Static field data
- Non-static data is called instance data
- If a class has non-static field each object of the type has its own copy of the field
- Obviously, you declare a field as static if its value will be common to all objects
- Static methods
- Can only work with static fields/data
- Call only call fellow-static methods
- Static constructors
- If you have a regular constructor affect static data then that value will be changed for all objects of the type every time you instantiate a new object!
- Often, though, you want to be able to set the value of a static field at runtime, rather than at design time
- C# allows you to do this via a static constructor
- Characteristics/syntax of a static constructor:
- You can only define a single static constructor (i.e., it cannot be overloaded)
- You do not use any access modifiers
- There are no parameters allowed
- It executes only once, regardless of the number of objects created
- The runtime invokes the static constructor either
- When it creates an instance of the class
- Before the first (i.e., any) static member is invoked by a caller
- It executes before any instance-level constructors
- Static classes
- A static class, of course, cannot be
new-ed
- If a class is defined as static all fields & members must be marked with the
static
keyword - If for some reason working pre-.NET 2.0 framework (when static classes were introduced) you create "static" classes by:
- Qualifying the default constructor as
private
(ergo, you cannotnew
that class) - Qualifying the class as
abstract
- Qualifying the default constructor as
- A static class, of course, cannot be
- Importing static members via the C#
using
keyword- New as of C# 6.0
- Syntax/example (note use of
static
keyword inusing
statements:
// Import static members of Console & DateTime…
using static System.Console;
using static System.DateTime;
static class TimeUtilClass
{
public static void PrintTime()
{ WriteLine(Now.ToShortTimeString(); }
public static void PrintDate()
{ WriteLine(Today.ToShortDateString(); }
} - Note: when using this approach you do need to be wary of static classes having duplicate method names
- General
- Constructors
- OOP
- Defining pillars of OOP
- Encapsulation
- Simplifies by hiding code
- Protects data
- Inheritance
- Use to establish is-a relationships
- Allows you to extend functionality
- Under code-reuse banner you also have has-a relationships
- (Has-a relationships have to do with containment & delegation rather than with inheritance)
- Polymorphism
- Relates to treating related classes/objects in a similar manner
- Polymorphic interface is the set of members available to all descendants of a class
- Create a class's polymorphic interface via constructing
- Virtual members
- Provide a default implementation
- May be changed/overridden
- Abstract members
- No default implementation provided
- However, a signature for the member is provided
- Must be overridden
- Virtual members
- Encapsulation
- C# access modifiers
- General
public
- Applies to types or type members
- Means: no restrictions
private
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: can only be viewed/accessed by the class or structure which defines the item
protected
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: access/visibility restricted to the type itself and any child class(es)
internal
- Applies to types or type members
- Means: only visible/accessible within the current assembly
protected internal
- Can be applied to type members or nested types (i.e., not to types themselves)
- Means: visible/accessible within:
- Defining assembly
- Defining class
- Derived classes
- Default access modifiers
- Types:
internal
- Type members:
private
- Types:
- Access modifiers & nested types
- Available modifiers for nested types:
private
protected
protected internal
- Ergo, non-nested types can only take the following modifiers
public
internal
- Available modifiers for nested types:
- General
- The first pillar: C#'s encapsulation services
- General
- Encapsulation based on protecting an object's internal data
- To manipulate object data want to force user to do so via:
- Accessor (a.k.a., getter)
- Mutator (a.k.a., setter)
- Use either of two methods to protect data:
- Define a pair of accessor/mutator methods
- Define a .NET property
- Encapsulation using traditional accessors & mutators
- Assume you have an
Employee
class with a privateempName
field - "Classically" you would write something like the following methods to read/write the employee's name
- Accessor:
public string GetName()
{ return empName; } - Mutator:
public void SetName(string name)
{
//Some validation code…
empName = name;
}
- Accessor:
- Assume you have an
- Properties
- Encapsulation using .NET properties
- In .NET you want to use properties rather than traditional get/set methods
- Syntax/Example:
public string Name
{
get { return empName; }
set
{
//some validation code…
empName = value;
}
} - Remember not to use parentheses to declare properties
- You have
get
scope &set
scope inside the property's scope - Declared data type in property must of course match the data type of the underlying field
- Note: within
set
scopevalue
token is not a C# keyword but a contextual keyword - .NET properties tend to be easier to work with than getter/setter methods
- Example (increasing an employee's age by 1 yr with getter/setter methods):
joe.SetAge(joe.GetAge() + 1);
- Example (increasing an employee's age by 1 yr with .NET properties):
joe.Age++;
- Example (increasing an employee's age by 1 yr with getter/setter methods):
- Using properties within a class definition
- Typically you include validation code in almost all "set" statements of your properties
- Rather than duplicate validation code within your constructors, pass your constructor parameters through your property definitions
- In fact, you typically want to run all internal data-manipulation through your property definitions within all of a class's methods
- Another way of saying this: as a rule it is only the properties themselves that should be allowed to touch a class's private fields
- Internal representation of properties
- CIL creates
get_
&set_
methods behind the scenes to handle .NET properties - Therefore, you will get compile errors if you create traditional accessor/mutator methods & name them with
get_
&set_
prefixes
- CIL creates
- Controlling visibility levels of property
get/set
statements- Unless you specify otherwise
get/set
statements "inherit" the visibility level of the container property - You can, though, specify a different - though only more restrictive - level for a getter or setter
- Unless you specify otherwise
- Read-only & write-only properties
- Achieve by simply omitting the appropriate
get/set
statement - If you make an property read-only then the only way to assign a value to the underlying field is through a constructor
- In that case you would allow a constructor access to the underlying field (i.e., rather than running the parameter through the associated property)
- Achieve by simply omitting the appropriate
- Static properties
- Must operate on static data
- Example: a company name for a/the
Employee
class - Static properties can, of course, only be read & set at the class level
- Probably easier to create a static constructor than go through the process of explicitly setting a static property
- Encapsulation using .NET properties
- General
- Defining pillars of OOP
- Creating classes - advanced
- Automatic properties
- General
- Introduced with .NET 3.5
- Used for those instances where you are not creating or employing any validation code
- Syntax/Example (for an Employee's name & age)
public string Name { get; set; }
public int Age { get; set; } - Limitation: the name of the auto-generated private field behind the property is not visible within the C# code base
- Code snippet: stubs out an automatic property for you
- Notes:
- Can specify access modifiers with automatic properties
- Can provide read-only automatic properties simply by omitting the set scope
- You may not, however, create a write-only automatic property
- Interacting with automatic properties
- Because the private backing fields are unavailable at the code level any members which interact with the "fields" will have to do so using the property names
- External interaction is unchanged
- Automatic properties & default values
- When using auto properties of numerical or boolean data types the properties are immediately assigned a default value
- However, if you use auto properties for, say, as an internal/nested class type, the hidden reference type will initially be set a value of
null
- This exposes you to the possibility of encountering a
null reference exception
at runtime if you try to invoke the property of the container class which "points to" the contained class - To ensure this doesn't occur you must instantiate the appropriate contained object(s) within the constructor of the container class
- Initialization of automatic properties
- New as of C# 6.0
- Allows you to
- Assign an initial value to a property other than its default value
- If the automatic property is of a reference type (e.g., an embedded class) you can now provide an instance of that class
- This obviates the risk of encountering a
null reference exception
at runtime
- Syntax/example:
class Garage
{
// Hidden backing field set to 1…
public int NumberOfCars { get; set; } = 1;
// Hidden backing field set to new Car object…
public Car MyAuto { get; set; } = new Car();
// Ctors, methods, etc….
}
- Creating immutable (i.e., read-only) automatic properties
- Option 1:
- Omit the
set;
statement - Example:
public string Name { get; }
- The immutable property then can only be set in the class's constructor
- Omit the
- Option 2:
- Declare the
set;
statement asprivate
- Example:
public string Name { get; private set; }
- Property can still be set via the class constructor
- Property can also be set via a method
- Under this option the property is still considered immutable because the class's consumers cannot change it
- Declare the
- Note: Should you desire, you can also supply a more restrictive access modifier (typically
private
) to either or both theget;
and/orset;
statements
- Option 1:
- General
- Object initialization syntax
- General
- When working with other people's classes you often want to set a number of properties upon creating an object yet find that the existing constructors do not allow that capability
- Object initializer syntax allows you instantiate an object and set property values on one line of code:
- Use brackets - i.e., { and } - instead of parentheses when new-ing an object
- Enclose a comma-delimited list of property/value assignments inside the brackets
- Syntax/Example:
Point myPt = new Point { X = 30, Y = 40 };
- Behind the scenes
- First invokes default constructor
- Then assigns property values
- Calling custom constructors with initialization syntax
- It can be done
- Call a custom constructor, and pass in values as normal
- Then, just after that bit of code add the comma-delimited property values inside brackets
- Syntax/Example (where color has been added as a property):
Point myPt = new Point("Black") { X = 30, Y = 40 };
- Note - earlier example could have been rewritten as:
//Explicitly invoke the default ctor…
Point myPt = new Point() { X = 30, Y = 40 };
- Initializing data with initialization syntax
- It is not at all uncommon to have a class with properties exposing another class or structure
- In this case you can simply nest your object initialization syntax
- Example:
- You have a
Point
structure whose two int properties are simplyX, Y
- You then have a
Rectangle
class whoseTopLeft
&LowerRight
properties are of typePoint
- Syntax/example:
// Create & initialize a Rectangle…
Rectangle myRect = new Rectangle
{
TopLeft = new Point { X = 10, Y = 10 },
BottomRight = new Point { X = 200, Y = 200 }
};
- You have a
- General
- Constant field data
- General
- Set by use of
const
keyword - Constant field data are implicitly static
- Constant field data is a case where you might want to avoid exposing the value as a property
- Inside a class could do something along the lines of
public const string CompName = "RCP Consulting, LLC"
- Because this public field name is defined as a constant external callers will not be able to modify it
- Inside a class could do something along the lines of
- If you decide to expose constant data through a property that property must be static
- You can also define constants within members (i.e., methods)
- Set by use of
- Read-only fields
- Use
readonly
keyword - Another case where you might want to declare the field
public
& avoid dealing with a property - Constant data values must be known & set at compile time
- Values for read-only fields, on the other hand, can be set at runtime
- Value can only be set within the scope of a constructor
- Attempts to set the value of a read-only field outside the scope of a constructor will result in a compile error
- Use
- Static read-only fields
- Unlike constant fields, read-only fields are not implicitly static
- You can, of course, set the value at compile time
- If you are setting the value at runtime you must (of course) do so inside a (the) static constructor
- General
- Partial classes
- Identified with
partial
keyword (must be used on every appropriate file/class definition) - If a class is large you can divvy it up across multiple
*.cs
files - Naming convention for files might be something like
MyClass.Internal.cs
, etc. - Typically, field data, constructors & properties are not modified much during development, so those are good candidates for an
*.internal.cs
file
- Identified with
- Documenting C# code via XML
- General
- Use triple-slash (
///
) notation to generate - See docs.microsoft.com > .NET > C# Guide > C# Programming Guide > XML Documentation
- Must include lines for:
- Opening XML tag
- XML content
- Closing XML tag
- However, VS will generate a lot of this commentary for you!
- Simply include
///
at the top of a class definition - VS will then generate opening & closing XML tags for
summary, parameters
, & (perhaps?) more
- Simply include
- XML comments can also be entered/created within VS's Class Details window
- Note: XML comments become visible from within VS's Intellisense
- Use triple-slash (
- Generating the XML file
- If using the command-line compiler (
csc.exe
) use the/doc
flag - In VS use the tab of the Properties window
*.xml
documentation files are created within the\bin\Debug
folder of your project
- If using the command-line compiler (
- Transforming XML code
- NDoc is one, though somewhat dated, utility
- Can also use Sandcastle, which was a MSFT tool
- General
- Automatic properties
- The C# class type
- Inheritance & Polymorphism
- The basic mechanics of inheritance
- Specifying the parent class of an existing class
- Use a colon (
:
) to indicate inheritance - Establishes a is-a relationship (a.k.a., classical inheritance)
- A child class has access to all public members of its parent class(es)
- Note: Although constructors are typically defined as public they are not inherited by child classes
- Use a colon (
- Regarding multiple base classes
- C# does not allow for multiple inheritance
- (MI is allowed in certain other languages)
- Interfaces, however, allow for behavior that ultimately mimics MI very closely
- The
sealed
keyword- Sealed classes cannot be inherited
- You typically seal utility classes
- The
System
namespace, for example, contains a large number of sealed classes - Structures
- As noted before, structures are implicitly sealed
- Structures also cannot be derived from classes
- All in all, structures cannot be involved at all in inheritance; if you need inheritance you must employ classes
- Specifying the parent class of an existing class
- Revising VS class diagrams
- To work with class diagrams:
- Class diagrams are files unto themselves & take the
*.cd
extension - To create class diagrams simply drag class(es) from Solution Explorer window onto diagram surface
- Shortcut: To copy all types onto class diagram surface at once:
- Click on the project within Solution Explorer
- A button will appear in the Solution Explorer window - click on that icon
- Note: If you use automatic properties syntax in your class there (of course) are no underlying fields to see in a class diagram
- The second pillar of OOP: the details of inheritance
- General
- It's not uncommon to want to use a class you created in an earlier project within a current one
- To do so simple select
*.cs
file(s) & navigate to appropriate - Reminder: update the
namespace
declarations within the imported files
- Controlling base class creation with the
base
keyword- Inherent inefficiency of creating a derived class constructor without using the
base
keyword- Within a child-class constructor you would initialize any parameter which belongs to the base class by invoking properties
- However, if any properties of the base class are read-only you are out of luck
- Further, unless you override things C# invokes the base class constructor anyway before a derived class custom constructor is invoked
- Therefore, you have an inefficient construct, which calls two constructors, plus makes calls to any inherited properties
- Using the
base
keyword makes things much more efficient- Syntax:
public DerivedClass(datatype param1, datatype param2,
datatype param3,…) : base(param1, param2)
{
//Handle the params unique to derived class…
PropOfChildClass = param3;
} - Note the similarity in syntax with
this
keyword & constructor chaining - Now you have just one constructor call
- Note:
base
keyword can be used with any public or protected method of a parent class - its use is not limited to constructors - Remember that employing this syntax will silently remove the default constructor of the child class; if desired, explicitly recreate
- As a rule, this is how you want to create constructors in derived classes
- Syntax:
- Inherent inefficiency of creating a derived class constructor without using the
- Keeping family secrets: the
protected
keyword- Allows child data to access field data without having to use base-class properties
- This can increase efficiency - it also creates some obvious problems re: circumventing validation rules
- You probably want to keep fields
private
- It is very common, however, to create methods as
protected
(i.e., methods you want available for internal workings of a class)
- You probably want to keep fields
- Adding a sealed class
- General
- Programming for containment/delegation
- General
- This concerns has-a relationships
- Containment: adding a field/property where the data type is of some class
- Delegation: adding public members that expose the contained type's properties & methods
- Containment/Delegation is the Aggregation model in design patterns
- Understanding nested type definitions
- This is a variation on the has-a relationship
- C# allows you to define any of the 5 .NET types inside the scope of a class or structure
- The nested type can itself be declared as either
public
(if the containing class is public) orprivate
- Rationales for creating nested types:
- You gain complete control over the access level of an inner type
- You cannot declare a non-nested type as
private
- You can, however, declare a class as
private
if it is nested inside another type
- You cannot declare a non-nested type as
- A nested type can access the private members of its containing type
- If you are designing a utility/helper class that is only going to be used by one other type it is probably best to nest that helper class inside the class it is designed to assist
- You gain complete control over the access level of an inner type
- Accessing an inner class from outside the container type:
- It is certainly do-able - provided the inner type is identified as
public
- You must then essentially work with the inner type as a property of the container
- Syntax/Example:
static void SomeMethod()
{
OuterClass.PublicInnerClass inner;
inner = new OuterClass.PublicInnerClass();
}
- It is certainly do-able - provided the inner type is identified as
- Not surprisingly, you can nest as many levels as you want
- General
- The third pillar of OOP: C#'s polymorphic support
- The
virtual
&override
keywords- Polymorphism allows for subclasses to define their own particular implementation of a parent/base class method
- Exact term for this is method overriding
virtual
keyword means that a method may (but does not have to be) overridden- If a subclass wants to take advantage of that opportunity it must employ the
override
keyword - Note: cannot mark a
static
method asvirtual
- In VS when you employ the
override
keyword to declare a method:- VS automatically includes
base.MethodYouAreOverriding
within your method scope - Therefore, you do not need to recreate the underlying code
- VS automatically includes
- Overriding virtual members using VS
- When overriding a method:
- Must re-use/re-declare all parameters & their types
- Must also re-declare any & all parameter passing conventions (i.e.,
ref, out, & params
)
- In VS, once you type
override
& then hit you are then presented with all overridable methods in your base class - When you then select a particular method & hit VS stubs out the method for you
- When overriding a method:
- Sealing virtual members
- You do this if at some point in the inheritance chain you don't want some member/method to be overridden any more
- You can override & seal all at once
- Syntax/Example:
public override sealed void SomeVirtualMethod() { /* code… */ }
- Abstract classes
- It's not uncommon to design a base class that you never want to see instantiated
- Often, a base class is simply too ill-defined to serve as an object
- In this case use
abstract
classes - these cannot be instantiated - Even when a class is abstract you typically want to give it constructors
- Abstract classes can certainly contain non-abstract members
- The polymorphic interface
- Abstract classes can contain
- Abstract members
- Virtual members
- 'Regular' members
- An abstract member:
- Does not supply any default implementation
- Must be accounted for by any & all derived class(es)
- Note: an abstract member can only be declared inside an abstract class
- Elements of an abstract member
- Name
- Return type (if any)
- Parameter set (if any)
- Note: Since there is no implementation to provide you omit scope brackets
- Example/Syntax:
public abstract void MyAbstractMethod();
- A polymorphic interface is the set of an abstract class's abstract & virtual methods
- Handling abstract methods in child classes
- Mark with the
override
keyword to implement - If a child class is itself abstract then it does not have to implement its parent's abstract methods
- Mark with the
- Abstract classes can contain
- Member shadowing
- Shadowing is when a child class defines a member with the same name used in a base/parent class
- This is most likely to occur when you are deriving from classes you, yourself, did not design
- 2 options when you want to shadow a member:
- 1) Use the
override
keyword- This, however, requires that you are able to modify the source code & mark the member you want to work with as
virtual
- Limitation, obviously, is that you often cannot modify the parent class's code
- This, however, requires that you are able to modify the source code & mark the member you want to work with as
- 2) Use the
new
keyword- The
new
keyword tells the compiler to ignore the base class's implementation of the method - You can in fact apply the
new
keyword to any derived type:field, constant, property,
etc.
- The
- 1) Use the
- Shadowing is also sometimes termed hiding a base-class member
- Lastly, if you decide you want access to the base-class member implementation after all? - Simply cast the derived object as an instance of the base class!
- The
- Base class/derived class casting rules
- General
- Base class for all classes in .NET is
System.Object
- You can always store a child class in a variable of some parent type - i.e., when the is-a relationship exists
- This is called an implicit cast
- Example:
//Assume Manager is derived from Employee…
Employee Bill = new Manager(Bill,…); - Note: Even though the variable
Bill
has been defined as anEmployee
type the object in memory to which it points is aManager
type - Benefit of implicit casting is that if you create a method which takes, say, an Employee as a parameter you can pass it a Manager, a SalesPerson, etc.
- As discussed earlier with datatypes, you also have explicit casts
- You must employ an explicit cast when you are going from parent class to child class
- Syntax/Example:
//To cast an Employee as a Manager…
(Manager)someEmployeeReference
- Base class for all classes in .NET is
- The C#
as
keyword- Casting is evaluated at runtime, not at compile time
- This obviously introduces the possibility of runtime errors if you make unworkable/inappropriate casts
- Can of course protect yourself by putting your cast statements inside
try/catch
blocks - Alternative: use
as
keyword & then test for anull
return value - Syntax/Example:
Manager bill = new Manager("Bill", other parameters…);
Circle c = bill as Circle;
if (c == null)
//Some code re: invalid cast…
- The C#
is
keyword- Returns
true/false
- Syntax/Example:
if (Bill is Manager) { //some code… }
- Returns
- General
- Master parent class:
System.Object
(incl.Person
example, Troelsen & Japikse: 7th Ed., pp. 239‑44)- General
- Virtual members
public virtual bool Equals(object obj);
- Default: return
true
if the items being compared refer to the identical item in memory - In other words, this compares object references, not the state of the object
- Method is generally overridden to test whether 2 objects have the same state (i.e., a value-based comparison)
- If you override
Equals()
you should also overrideGetHashCode()
! - Note: as discussed previously, this method is overridden in the
ValueTypes
class to make value-based comparisons
- Default: return
protected virtual void Finalize();
- Used to free unallocated resources before an object is destroyed
- This is covered in section on CLR garbage collection
public virtual int GetHashCode();
public virtual string ToString();
- Default: returns fully qualified name i.e.,
<namespace><type name>
- Often "overridden by a subclass to return a tokenized string of name/value pairs that represent the object's internal state…" (Troelsen & Japikse: 7th Ed., Table 6-1, p. 239)
- Default: returns fully qualified name i.e.,
- Instance-level, non-virtual members
public Type GetType();
(a Runtime Type Identification - RTTI - method)protected object MemberwiseClone();
- Static members
public static bool Equals(object objA, object objB);
public static bool ReferenceEquals(object objA, object objB);
- Virtual members
- Overriding
System.Object.ToString()
- Recommended approach:
- Semi-colon delimited name/value pairs for every state in class
- Wrap entire list in square brackets - "
[
" and "]
" - This convention is followed by many types in .NET base class libraries
- Syntax/Example:
public override string ToString()
{
string myState;
myState = string.Format("[First Name: {0};
Last Name: {1}; …", FirstNm, LastNm, …]);
return myState;
}
- Note: when overriding
ToString()
should remember to include state data up the chain of inheritance- First run
base.ToString()
- Then append your sub-class's data
- First run
- Recommended approach:
- Overriding
System.Object.Equals()
- Assume we are overriding for value-based equality
- Since this method takes a parameter of type
object
a smart developer will check 2 things:- The object is the appropriate type
- The variable is not a
null
reference - Syntax/Example:
public override bool Equals(object obj)
{
if (obj is YourClass && obj != null)
{
YourClass temp;
temp = (YourClass)obj;
if (temp.Property1 == this.Property1 && …)
{ return true; }
else
{ return false; }
}
return false;
}
- In a large class with many fields this approach can be tedious
- Alternative:
- If you have a well-implemented
ToString()
method simply test equality of the returned strings - Because
ToString()
is a method ofSystem.Object
you don't even have to cast theobject
passed in as a parameter to see if it is of the correct type!
- If you have a well-implemented
- Overriding
System.Object.GetHashCode()
- A hash code is a numerical value that represents an object in a particular state
- Default: system uses object's current location in memory to generate hash value
- If you intend to store a custom class in a
Hashtable
type (this resides in theSystem.Collections
namespace):- You should override
GetHashCode()
Hashtable
invokes bothEquals()
&GetHashCode()
to identify correct object- Note re:
System.Collections.Hashtable
classGetHashCode()
is called 1st, & provides general location of the objectEquals()
is then invoked to determine exact match
- You should override
- Generating a hash code
- A variety of algorithms exist
- Easiest approach is to leverage the hash code algorithm behind
System.String
'sGetHashCode()
- If your class contains a string field that will be unique for each object (such as SSN of a
Person
object) simple do something likereturn SSN.GetHashCode();
- Failing that simply employ
return this.ToString().GetHashCode();
- Good Eric Lippert article (from 2011) on using GetHashCode()
- Static members of
System.Object
- These, of course, cannot be overridden
- Especially when you have overridden the
Equals()
method in a custom class to check for value-based equalitySystem.Object.ReferenceEquals()
allows you to see whether two variables point to the same object in memory
- General
- The basic mechanics of inheritance
- Covariance & contravariance (aka, variance)
- General
- Closely related to casting
- Assignment compatibility
- Essentially another phrase for implicit casting
- Example:
string str = "test";
object obj = str;
- Covariance is a feature which allows for implicit reference conversion when dealing with parent/child types
- "Covariance preserves assignment compatibility and contravariance reverses it." (docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > Covariance and Contravariance)
- Covariance & contravariance available with
- Array types
- Generic type arguments
- Delegate types
- Arrays
- "Covariance for arrays enables implicit conversion of an array of a more derived type to an array of a less derived type." (docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > Covariance and Contravariance)
- Example:
object[] array = new String[10];
- Note: this construct is not type safe, so you will get a run-time error if you then implement the following line of code:
array[0] = 10;
- General
- Structured Exception Handling
- Overview: errors, bugs, & exceptions
- Defined
- Errors: user (generally input) errors
- Bugs: programming errors
- Exceptions: corrupted file, data connection not available, etc.
- .NET handles all 3 as exceptions
- .NET base class libraries contain a wide range of
Exceptions
- Note: .NET convention is to end all exception names with the word
Exception
- a custom which should be followed when creating custom exceptions
- Defined
- .NET exception handling
- General
- Error-handling in Windows programming prior to .NET used a variety of techniques
- Windows API itself identifies errors in a variety of ways
- COM developers often make use of interfaces to handle exceptions
- .NET introduced/makes use of structured exception handling (S.E.H.)
- Many other languages simply return a number to identify an error
- .NET exceptions are objects unto themselves & thus can contain a wealth of information
- The atoms of .NET exception handling
- 4 involved entities:
- "A class type that represents the details of the exception" (Troelsen & Japikse: 7th Ed., p. 249)
- "A member that throws an instance of the exception to the caller" (Troelsen & Japikse: 7th Ed., p. 249)
- The calling code
- A block of code within the caller that is designed to catch any thrown exceptions
- All .NET exceptions ultimately derive from
System.Exception
- 4 involved entities:
- The
System.Exception
base class- Important class components:
public class Exception : ISerializable, _Exception
{
//Ctors (partial list)…
public Exception(string message, Exception innerException);
public Exception(string message);
public Exception();
//Methods…
public virtual Exception GetBaseException();
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context);
//Properties (partial list)…
public virtual IDictionary Data { get; }
public virtual string HelpLink { get; set; }
public Exception InnerException { get; }
public virtual string Message { get; }
public virtual string Source { get; set; }
public virtual string StackTrace { get; }
public MethodBase TargetSite { get; }
} - Comments:
- The
_Exception
interface allows a .NET exception to be recognized & handled by unmanaged code (esp. COM) - Many methods & properties are virtual & can thus be overridden
- The
- Important class components:
System.Exception
class propertiesData
- Retrieves a collection of key/value pairs (in an object implementing
IDictionary
) - Empty by default
- You can populate this property with effectively unlimited info
- Retrieves a collection of key/value pairs (in an object implementing
InnerException
- Contains exception which caused "this" exception
- Prior exceptions are passed in via the constructor
Message
- passed in as a constructor parameterSource
- Name of the assembly or object that threw the current exception
- Read/write
StackTrace
- obviously invaluable in debuggingTargetSite
- The
MethodBase
object returned by this property contains lots of information about the method which threw the current exception - Invoke
ToString()
onMethodBase
object to access the info
- The
- General
- A simple example (see Troelsen & Japikse: 7th Ed., pp. 251‑3)
- Throwing a general exception
- Can make use of
string.Format()
to setMessage
property when instantiatingException
object - Use
throw new Exception(…)
- Use
try/catch
blocks in the calling code - Notes:
- In this approach you decide the circumstances which merit throwing an exception
- In practice, you of course really only want to throw an exception to address situations you cannot resolve within your code
- Can make use of
- Catching exceptions
- Syntax (of simple
catch
block):
catch(Exception e)
{ //Exception handling code… } - Code continues after the
catch
block - unless, of course, that block terminates things
- Syntax (of simple
- Throwing a general exception
- Exceptions
- Configuring the state of an exception
- General
- Not all properties can be set with a constructor
- 2 options:
new
theException
object, set properties, & then throw thatException
objectnew
theException
object using object initialization syntax
- The
TargetSite
property- The returned
MethodBase
object alone gives you the following info on the culprit-calling method:- Name
- Return type
- Parameter types
- Other useful properties of
MethodBase
objectDeclaringType
(name of class)MemberType
(Method, Property, etc.)- Syntax/Example:
e.TargetSite.DeclaringType;
- The returned
- The
StackTrace
property- Gives you full path of calling method
- Also gives you line numbers
- Bottom-most line of output == first calling method, etc.
- The
HelpLink
property- Empty string by default
- Use to provide link to a help file/url
- Cannot set this property inside the
Exception
constructor
- The
Data
property- Great, all-purpose property for adding custom data
- Cannot set this property inside the
Exception
constructor - Because you must invoke
Add
method of property in this case you must new theException
object before throwing it - Syntax/Example:
Exception ex = new Exception();
ex.Data.Add("Cause", "Some explanation…");
ex.Data.Add("TimeStamp",
string.Format("Occurred: {0}", DateTime.Now));
throw ex; - Must include a
using System.Collections;
statement in your namespace to iterate over results in acatch
block - Syntax/Example (for iterating over
Data
property incatch
block):
catch (Exception e)
{
if(e.Data != null)
{
foreach (DictionaryEntry de in e.Data)
//Process de values…
}
} - Note: despite all of the flexibility offered by the
Exception.Data
property, developers tend to build customException
objects (discussed below) when they want to include custom information
- General
- System-level exceptions (
System.SystemException
)- These are exceptions thrown by the .NET platform
- Typically regarded as fatal & non-recoverable errors
- Derives from
System.Exception
, but adds absolutely no functionality - Purpose of creating this class is to allow you to distinguish system errors from app errors
- Allows you to test errors (
if (e is SystemException)…
)
- Application-level exceptions (
System.ApplicationException
)- General
- Derives from
System.Exception
, but, as withSystem.SystemException
, adds absolutely no functionality - You should derive any custom exceptions from this class
- You tend to build these only "when the error is tightly bound to the class issuing the error…" (Troelsen & Japikse: 7th Ed., p. 263)
- Derives from
- Building custom exceptions - possibilities
- Can always throw a
System.Exception
in your code - Often, however, it is more advantageous to throw a strongly-typed exception
- .NET Best Practices:
- .NET Best Practices: Any custom exception should end with the suffix "Exception"
- All custom exceptions should be
public
, so that they can be recognized when called outside of an assembly
- Possible approaches:
- Override the
Message
property (often usingstring.Format()
) - Add properties for information that you otherwise jam into the
Data
property
- Override the
- Note: assigning values to custom properties is a little easier than populating the
Data
property - Your
catch
block can now be written to catch an application-specific error
- Can always throw a
- Building custom exceptions - in practice
- You typically don't override the
Message
property - simply call on the message property via a call tobase
- You also typically don't add custom properties
- Simply build a custom class, which allows your
catch
block to immediately recognize source of error
- You typically don't override the
- Building custom exceptions - .NET best practices
- Requirements:
- Derives from
ApplicationException
- Is marked with
[System.Serializable]
attribute - Define the following constructors:
- Default (
public
) - One (
public
) that sets the inheritedMessage
property - One (
public
) to handle inner exceptions - One (
protected
) to handle the serialization of your type
- Default (
- Derives from
- See Troelsen & Japikse: 7th Ed., p. 264, for an example
- Note: VS has a code snippet template for all this -
- Requirements:
- General
- Configuring the state of an exception
- Handling exceptions
- Processing multiple exceptions
- General
- You can, & should, include multiple
catch
blocks, each one suited to a particular type of exception - When doing this arrange your
catch
blocks from the specific to the general, as the processor will stop at the 1stcatch
block that works - Note: when using multiple
catch
blocks you will in fact get a compiler error if your first block catches(Exception e)
, as now none of the othercatch
blocks is reachable - When using multiple
catch
blocks it probably doesn't hurt to make the last onecatch (Exception e)
- Best practice: be as specific as possible about the types of exceptions you are handling in your
catch
blocks
- You can, & should, include multiple
- General
catch
statements- You don't, in fact, have to specify the type of error you are catching
- Though not very useful, you can write:
catch { /* error handling code… */ }
- Rethrowing exceptions
- Typically done to push an error up the call stack
- Re-throwing is (of course) done inside a
catch
block - Syntax is simple:
throw;
(i.e., without any arguments) - this preserves the original error information
- Inner exceptions
- Though you really want to try your best to avoid this, it is, of course, possible to generate a new exception whilst in a
catch
block handling an existing problem - Best Practice (in this scenario):
- Catch the new error in an object that is of the same type as the original
Exception
- However, make the new error an inner exception of the new object
- Note: you create a new object because the only way to create an inner exception is as a constructor parameter
- Syntax/Example:
catch(MyCustomException e)
{
try
{ /* Some error-handling code… */ }
catch (Exception e2)
{
throw new MyCustomException(e.Message, e2);
}
}
- Catch the new error in an object that is of the same type as the original
- Though you really want to try your best to avoid this, it is, of course, possible to generate a new exception whilst in a
- The
finally
block- This block of code is optional
- Code that will still execute after an exception has been handled
- Use to close files, data connections, etc., etc.
- Exception filters
- New as of C# 6.0:
when
keyword - A
when
clause is placed on acatch
scope - Notes re: the conditions of a
when
clause- Must evaluate to a Boolean
- Must be wrapped inside parentheses
- You do not include a semi-colon in the when clause
- If you have a complex exception create a custom method to call from within the
when
clause
- Syntax/example (assume you have created a custom
ApplicationException
with a custom property called ErrorTimeStamp of typeDateTime
:
catch (MyCustomException e) when
(e.ErrorTimeStamp.DayOfWeek != DayOfWeek.Sunday)
{
// Catch block will not run on Sundays…
}
- New as of C# 6.0:
- General
- Who is throwing what?
- .NET Framework SDK documents any & all exceptions that any member of a .NET base class library can throw
- Inside VS, can also get this information by hovering mouse over base class member in code
- Debugging unhandled exceptions using VS
- Run - code will break where exception is encountered
- You then get a pop-up window -
- Window shows value of
Message
property - is an especially useful link
- Window shows value of
- Processing multiple exceptions
- Corrupted state exceptions (CSE)
- These are essentially low-level Windows API errors
- "Simply put, if the Windows OS sends out a corrupted state error, your program is in very bad shape." (Troelsen: 5th Ed., p. 285)
- If you encounter one of these bad boys you have no choice but to close down the program
- Prior to .NET 4.0
- These errors could be caught using a
System.Exception
catch block - However, there wasn't much you could do to handle them
- These errors could be caught using a
- With .NET 4.0
- New namespace:
System.Runtime.ExceptionServices
(in themscorlib.dll
) - This new namespace is very small - in fact it contains only 2 class types
- Now,
System.Exception
catch blocks will not automatically handle CSEs - To handle CSEs in your catch blocks must apply the
[HandleProcessCorruptedStateExceptions]
attribute to the appropriate method(s) - See Troelsen: 5th Ed., pp. 285‑7, for more details
- New namespace:
- Overview: errors, bugs, & exceptions
- Interfaces
- Interface basics
- General
- "An interface is nothing more than a named set of abstract members." (Troelsen & Japikse: 7th Ed., p. 275)
- As abstract, there are no default implementations
- "An interface expresses a behavior that a given class or structure may choose to support." (Troelsen & Japikse: 7th Ed., p. 275)
- A class or structure may support multiple interfaces
- Note: .NET convention is to begin all interfaces with the letter
I
- .NET base class libraries, of course, have plenty of built-in interfaces
- Interface types vs. abstract base classes
- Abstract (base) classes can contain:
- Abstract members (to provide a polymorphic interface)
- Constructors
- Field data
- Nonabstract members (with implementation)
- Interfaces, however, can only contain abstract member definitions
- Polymorphic support
- With abstract classes you are limited to that class's derived types
- Interfaces, however, can be applied across class hierarchies (i.e., they are highly polymorphic)
- Another limitation of abstract classes:
- Every derived class must provide an implementation of the base class's abstract members
- With interfaces you can cherry-pick the classes which must support the abstract methods you want implemented
- Abstract (base) classes can contain:
- General
- Custom interfaces
- Defining custom interfaces
- Use
interface
keyword - Interfaces never specify a base class (though you can derive from another interface)
- Never specify an access modifier for interface members - they are implicitly
public & abstract
- Never provide implementation details
- Other than methods, interfaces can specify:
- Property prototypes (including whether read-only or write-only)
- Event definitions
- Indexer definitions
- You cannot "new" an interface
- Example:
// This will throw a compiler error…
IMyInterface mi = new IMyInterface(); - Example (explicit casting can be done with interfaces, though):
// Often need to do this if class has employed
// "explicit interface implementation" (see below)…
IMyInterface mi = (IMyInterface)someObject;
- Example:
- Use
- Implementing an interface
- Listed after the colon operator in the type definition
- Interfaces are listed after any base-class listing
- Listings are comma-delimited
- When implementing an interface a type must implement all members of that interface
- Listed after the colon operator in the type definition
- Working with interfaces
- Invoking interface members at the object level
- No differences from invoking regular members
- Options when you are unsure of whether an object implements an interface
- "Naïve" approach
- Attempt an explicit cast
- Wrap that cast in a
try
block catch
forInvalidCastException
- Use the
as
keyword- Example:
SomeClass sc = new SomeClass();
ISomeInterface Itemp = sc as ISomeInterface;
if (ITemp != null)
{ // further code… } - What's nice about this approach is that it obviates the need for a
try/catch
block
- Example:
- Obtaining interface references: the
is
keyword- Use an
if
statement - Example:
SomeClass sc = new SomeClass();
if (sc is ISomeInterface)
{ // further code… } - Again, obviates need for a
try/catch
block
- Use an
- "Naïve" approach
- Interfaces as parameters - can be done, just as with any other type or datatype
- Interfaces as return values - can be done
- Arrays of interface types
- Can be constructed
- These tend to be very powerful programming constructs as they allow you to deal with objects across various class hierarchies
- Invoking interface members at the object level
- Implementing interfaces using VS
- When you declare a class implementing an interface:
- The letter
I
of the interface name will be underlined in VS - The underline is a smart tag
- Clicking on the smart tag gives you a pop-up box
- After making either choice in the pop-up box the interface will be stubbed out for you in the class
- The letter
-
menu
- There is an option in VS's menu
- Pulls a new interface definition out of existing code
- When you declare a class implementing an interface:
- Explicit interface implementation
- When implementing multiple interfaces in a class or structure there exists the possibility that the interfaces contain identical members
- When you encounter name clashes you need to use explicit interface implementation
- Example: You have a
Draw()
method withinIDrawToPrinter
&IDrawToForm
interfaces - Syntax (to distinguish between implementations):
class SomeClass : IDrawToForm, IDrawToPrinter
{
void IDrawToForm.Draw() { /* Implementation... */ }
void IDrawToPrinter.Draw() { /* Implementation... */ }
} - Notes:
- You do not supply an access modifier with explicit interface implementations
- These implementations are implicitly
private
- To access the methods (which, as
private
, now cannot be accessed via dot operator) you must explicitly cast your object
- "Second" use of explicit interface implementation
- Allows you to hide members that you don't want visible from normal dot notation
- However, a more sophisticated user of your object can gain access to the method via an explicit cast
- Designing interface hierarchies
- General
- These work just like class hierarchies
- You do not re-declare the methods from the base interface inside the derived interface
- A class or structure which implements a derived interface will have to implement all of the methods up the interface inheritance chain
- Multiple inheritance with interface types
- Perfectly ok
- Wrinkle with name-clashing methods:
- You have 1 interface which inherits from another 2 interfaces
- Those 2 interfaces have identically named methods
- You define a class or structure which implements the 1 child interface
- How does that class or structure implement the name-clashing method?
- Two possible implementations:
- 1) The class or structure can simply implement 1 version of the name-clashing methods
- 2) The class or structure can use explicit interface implementation so that 2 versions are available
- Note: Under Option 2 the object or structure would have to be explicitly cast to access the appropriate name-clashing method
- General
- Defining custom interfaces
- Essential .NET base class libraries interfaces
- Building enumerable types (
IEnumerable
&IEnumerator
)- General
- Both
IEnumerable & IEnumerator
are found in theSystems.Collections
namespace - Using
foreach
keyword/construct- Typically used to iterate through an array
- However,
foreach
can handle any type which supports aGetEnumerator()
method
- Relationship between
IEnumerable
&IEnumerator
GetEnumerator()
is the one & only method identified byIEnumerable
GetEnumerator()
has a return type, & that type isIEnumerator
- Definition:
public Interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
} - Implementing the two interfaces
- "Theory":
- You can develop customized implementations of
MoveNext()
, etc. - You can also "manually" get a hold of
IEnumerator
& work with that - Syntax/Example -
Widgets (IEnumerable)
"collection" class:
IEnumerator i = Widgets.GetEnumerator();
i.MoveNext();
Widget w = (Widget)i.Current;
- You can develop customized implementations of
- Practice: use the functionality built into
System.Array
- Assume you have a "container" class
- Essentially, you turn this class into a wrapper around an
array
- Then send the
GetEnumerator()
request into an embedded/private array - Syntax/Example:
//Container class for Widget objects…
public class MyWidgets : IEnumerable
{
private Widget[] wdgtArr = new Widget[4];
//Ctor…
public MyWidgets()
{ /* populate wdgtArr with new widgets */ }
//Now implement IEnumerable…
public IEnumerator GetEnumerator()
{
//Simply invoke method on array…
return wdgtArr.GetEnumerator();
}
} - Note: If you want to "hide" the
GetEnumerator()
at the object level use an explicit interface implementation
- "Theory":
- Both
- Building iterator methods with the
yield
keyword- An alternative to using
foreach
construct is to use iterators - Note:
yield
keyword is, in fact, never used alone - usage is alwaysyield return
- Iterators allow you to control how items are returned within a
foreach
construct - Implementation specifics:
- Your "container class" does not implement
IEnumerable
! - Iterator method must still be named
GetEnumerator()
- Return type must still be
IEnumerator
- Your "container class" does not implement
- Syntax Option 1 (using
foreach()
construct):
public class Widgets
{
private Widget[] myWdgts =
new Widget[]{ /*populate array… */ };
public IEnumerator GetEnumerator()
{
foreach(Widget w in myWdgts)
{ yield return w }
}
} - Syntax Option 2 (sans
foreach()
construct):
public class Widgets
{
private Widget[] myWdgts =
new Widget[]{ /*populate array… */ };
public IEnumerator GetEnumerator()
{
yield return myWdgts[0]
yield return myWdgts[1]
//etc…
}
} - Notes:
- In either case after code reaches
yield return
the CLR keeps track of the location within the array for the next call intoGetEnumerator()
(via "hidden" CIL code) - Option 2 obviously becomes unwieldy with an array of any size
- In either case after code reaches
- An alternative to using
- Building a named iterator
yield
(i.e.,yield return
) keyword(s) can be used inside any method- In other words, you are not constrained to the
GetEnumerator()
method - If you create another method for
yield/yield return
that method must still return an interface - However, you return an
IEnumerable
type, not anIEnumerator
return type - Syntax/example:
// Inside, say, a WidgetsCollection class…
public IEnumerable GetWidgets(bool ReverseOrder)
{
if ReverseOrder
{
for (int i = wdgtArray.Length; i !=0; i--)
{ yield return wdgtArray[i-1]; }
}
else
{
foreach (Widget w in wdgtArray)
{ yield return w; }
}
}
- In other words, you are not constrained to the
- Advantages of creating a named iterator:
- Once you move away from
GetEnumerator()
you can pass parameters into your iterator - The reason for a parameterized named iterator is that you can create multiple (i.e., customized) iterators
- Examples: methods which return items in reverse order, which return every 3rd item, etc.
- See Troelsen & Japikse: 7th Ed., p. 301, for code examples
- Once you move away from
- Calling a named iterator
- That a named iterator returns
IEnumerator
rather thanIEnumerable
of course affects how you call a named iterator - Syntax/Example:
foreach(Widget w in Widgets.YourNamedIteratorMethod(parameters…))
- That a named iterator returns
- Internal representation of an iterator method
- Compiler "generates a nested class definition within the scope of the defining type" (Troelsen & Japikse: 7th Ed., p. 348)
- That auto-generated nested class in turn implements
GetEnumerator()
MoveNext()
Current
property- (But not
Reset()
!)
- General
- Building cloneable objects (
ICloneable
)- Basic
- Can always get a shallow copy via
MemberwiseClone()
method ofSystem.Object
- Method is protected, though, so object users can't call it
MemberwiseClone()
is useful during cloning processICloneable
specifies a single method,Clone()
- Note:
- Troelsen (Troelsen & Japikse: 7th Ed., p. 350) points out that there is nothing in the
ICloneable
specification requiring thatClone()
return a deep copy of an object - Troelsen also notes that this has created debate & confusion in the .NET community
- Troelsen (Troelsen & Japikse: 7th Ed., p. 350) points out that there is nothing in the
- Basic approach behind
Clone()
:- Create a new, internal instance of the class
- Update the copy's properties
- Return that object to the
Clone()
invoker - Notes:
- When setting the new object's property remember to use
this
keyword to get containing object's properties - The
Clone()
method returns anobject
-the calling procedure must then cast theobject
to the appropriate type
- When setting the new object's property remember to use
- Streamlined approach: If (and only if) your type has no internal reference type variables simply invoke
MemberwiseClone()
- Can always get a shallow copy via
- Advanced (i.e., when your type contains reference-type variables)
- Typical implementation (of
Clone()
method):- Create a internal shallow copy of the class using
MemberwiseClone()
- Create any new instances of internal types
- Set the properties of those internal types (e.g.,
newInternalType.Ppty = this.InternalType.Ppty
) - Return the updated copy of this type
- Create a internal shallow copy of the class using
- Again, if your type has no internal reference types simply rely on
MemberwiseClone()
- Typical implementation (of
- Basic
- Building comparable objects (
IComparable
&IComparer
)IComparable
- Definition:
public Interface IComparable
{
int CompareTo(object o);
} - Typical implementation (comparing on PptyX):
int IComparable.CompareTo(object obj)
{
MyObj temp = obj as MyObj;
if (temp != null)
{
if (this.PptyX > temp.PptyX)
return 1;
if (this.PptyX < temp.PptyX)
return -1;
else
return 0;
}
else
throw new ArgumentException("Wrong parameter type…");
}
} - All basic data types already implement
IComparable
. Therefore… - Streamlined implementation (if your property is, say, an
int
):
int IComparable.CompareTo(object obj)
{
MyObj temp = obj as MyObj;
if (temp != null)
return this.PptyX.CompareTo(temp.PptyX)
else
throw new ArgumentException("Wrong parameter type…");
}
- Definition:
- Specifying multiple sort orders
IComparer
IComparer
is inSystem.Collections
namespace- Definition:
Interface IComparer
{
int Compare(object o1, object o2);
} - You generally do not implement on the class type you are interested in comparing
- Instead, you implement this on helper/utility classes, building one for each sort order (i.e., the property on which you want to run comparisons)
- Typically want to name your helper class
…Comparer
- Example:
public class WidgetNameComparer : IComparer
int IComparer.Compare(object o1, object o2)
{
Widget w1 = o1 as Widget;
Widget w2 = o2 as Widget;
if(w1 != null && w2 != null)
return String.Compare(w1.ItemName, w2.ItemName);
else
throw new ArgumentException("Parameter not a Widget!");
}
} - Using
System.Array
System.Array
has several overloadedSort()
methods- One such
Sort()
method takes an object implementingIComparer
- Specifically, you pass in your collection object and your
…Comparer
object - See Troelsen & Japikse: 7th Ed., p. 311, for an example of how to implement
- Custom properties, custom sort types
- Can create a custom, static property to help user sort your objects
- Example:
public class Widget : IComparer
{
…
// Read-only property returning …Comparer class…
public static IComparer SortByItemName
{ get { return (IComparer)new WidgetNameComparer(); } }
}
- Can now use:
Array.Sort(myWidgets, Widget1.SortByItemName);
- Building enumerable types (
- Interface basics
- Core Constructs - Part I
- Advanced C# Programming Constructs
- Collections & Generics
- General
System.Collections
namespace was included in very 1st release of .NETSystem.Collections.Generic
appeared with release of .NET 2.0- Constraints (as implemented by the
where
keyword) are an important facet of generics
- Collections
- Motivation behind collection classes
- General
- Most rudimentary .NET object container is
System.Array
class- Used to define a set of identically-typed items
- Fixed upper limit
- Often, however, need ability to:
- Grow & shrink container
- Create more elaborate screening of types of contained objects
- One step up from
System.Array
isSystem.Collections
- Collections in this namespace typically work only with
System.Object
types - As such, not ideal for type safety
- Collections in this namespace typically work only with
- Most rudimentary .NET object container is
System.Collections
namespace- Classes, & the interfaces they implement
ArrayList
- dynamically sized collection of objects, in sequential orderIList
ICollection
IEnumerable
ICloneable
Hashtable
- collection of key/value pairs, organized on the hash code of the keyIDictionary
ICollection
IEnumerable
ICloneable
Queue
- standard, FIFO queueICollection
IEnumerable
ICloneable
SortedList
- likeHashtable
, but sorted by key value & accessible via key value or indexIDictionary
ICollection
IEnumerable
ICloneable
Stack
- LIFO collection, with push, pop, & peek capabilitiesICollection
IEnumerable
ICloneable
- Interfaces
ICollection
- defines general characteristics of a collection, e.g. size, enumeration, & thread safetyICloneable
- see Building cloneable objectsIDictionary
- requires that a collection represent its contents via key/value pairsIEnumerable
- see Building enumerable types (IEnumerable
&IEnumerator
)IEnumerator
- see Building enumerable types (IEnumerable
&IEnumerator
)IList
- specifies behavior required to add, remove, & index items in a sequential list of objects
- Classes, & the interfaces they implement
System.Collections.Specialized
namespace- Contains (obviously) non-standard collection types, such as:
HybridDictionary
ListDictionary
StringCollection
BitVector32
- Contains other interfaces & abstract base classes
- Contains (obviously) non-standard collection types, such as:
- Other namespaces
System.Collections.ObjectModel
System.Collections.Concurrent
- used in a multithreaded environment
- General
- The issues with non-generic collections
- General
- There are issues (discussed below) with:
- Performance - especially when working with numerical data
- Type safety
- Any project created with .NET 2.0 or later should ignore these classes and instead use the collection classes in
System.Collections.Generic
namespace
- There are issues (discussed below) with:
- Performance
- Boxing
- "Boxing can be formally defined as the process of explicitly assigning a value type to a
System.Object
variable." (Troelsen & Japikse: 7th Ed., p. 320) - Example:
…
int myInt = 25;
object myBoxedInt = myInt;
… - CLR process
- Creates new object on the heap
- Copies value into that object
- Creates pointer to the object
- "Boxing can be formally defined as the process of explicitly assigning a value type to a
- Unboxing
- "Unboxing is the process of converting the value held in the object reference back into a corresponding value type on the stack." (Troelsen & Japikse: 7th Ed., p. 320)
- Operative line of code would be:
int unboxedInt = (int)boxedInt;
- CLR process
- First verifies that receiving data type is the same as the boxed type
- Copies value into that stack-based variable
- Challenges
- When unboxing you must do so into an appropriate variable type or you will raise an
InvalidCastException
- To handle this possibility you would typically wrap the unboxing code within a try/catch block
- When unboxing you must do so into an appropriate variable type or you will raise an
- Significance of boxing/unboxing with non-generic collections
- Non-generic collection classes operate on objects
- Even when creating, say, an
ArrayList
of integers behind the scenes the CLR must box each integer before passing it into theArrayList
- When retrieving those integers from the
ArrayList
would therefore need to cast each item back asint
variables - When working with a lot of data, all of the CLR steps required to box/unbox can slow down your app
- Boxing
- Type safety
- Because non-generic collection classes take objects, those collections can wind up holding anything & everything in the .NET world
- Further, there is no constraint that the 'items' within a given such collection are even of the same type (other than being of
System.Object
) - Prior to generics the method for ensuring type-safety:
- Create a custom collection class, e.g.
WidgetCollection
- Implement
IEnumerable
interface with class - Create a private, say,
ArrayList
inside theWidgetCollection
class - Create custom public methods, e.g.,
AddWidget(Widget w)
, which was nothing more than a wrapper method for, say,arWidgets.Add(w)
- Create a custom collection class, e.g.
- While this approach ensured type safety it had a big drawback
- You needed to create a custom class for every collection of every type of object
- Even though the underlying code & logic was essentially the same, you needed to create, say,
WidgetCollection, EmployeeCollection, ToolCollection
, etc.
- Further, you were still incurring boxing/unboxing penalties for collections of value-type items!
- General
- Motivation behind collection classes
- Generics
- General
- Solve the above problems (including no boxing/unboxing)
- Largely eliminate need to build custom collection classes
- Guarantee type safety
- Generic type parameters
- General
- Classes, structures, interfaces & delegates can be written generically
- Enums, however, cannot
- Nomenclature of generics
- The
T
in, say, theList<T>
class is referred to as a:- Token
- Type parameter
- "Placeholder"
- Read/pronounce
<T>
as "of T" - Read/pronounce
List<T>
as:- "List of T"
- "List of type T"
- Read/pronounce
List<Widget> myWidgets = new List<Widget>();
as:- "A List<> of T, where T is of type Widget"
- "A List of Widget objects"
- The
- Conventions
- Use
T
to represent types - Use
TKey
orK
to represent keys - Use
TValue
orV
to represent values
- Use
- Specifying type parameters for generic classes / structures
- "After you specify the type of parameter of a generic item, it cannot be changed…" (Troelsen & Japikse: 7th Ed., p. 329)
- "When you specify a type parameter for a generic class or structure,[sic] all occurrences of the placeholder(s) are now replaced with your supplied value." (Troelsen & Japikse: 7th Ed., p. 329)
- If you look at, say,
List<T>
, in in VS you find the placeholderT
throughout the type definition - When you define, say,
List<Widget>
, effectively theT
placeholder is replaced withWidget
- Specifying type parameters for generic members
- Non-generic classes & structures can contain & support generic members
- Must specify placeholder value at time you invoke the method / set property
- Specifying type parameters for generic interfaces
- There are many generic counterparts to non-generic interfaces - e.g.,
IComparable<T>
- Huge advantage of generic interfaces is that you then do not have to implement any casting operations, runtime checks, etc. on object type
- There are many generic counterparts to non-generic interfaces - e.g.,
- General
- The
System.Collections.Generic
namespace- General
- Namespace is defined in
mscorlib.dll
&System.dll
assemblies - The namespace contains replacements for most of the non-generic collection classes
- Counter-intuitively, many generic collection classes extend their non-generic counterparts (this allows the generic counterparts to work with legacy code)
- Namespace is defined in
- Generic interfaces
- List
ICollection<T>
IComparer<T>
IDictionary<TKey, TValue>
IEnumerable<T>
IEnumerator<T>
IList<T>
ISet<T>
- allows for the abstraction of sets
- Covariance
- The
T
,TKey
, &TElement
are covariant in the following generic interfaces:IEnumerator<T>
IEnumerable<T>
IQueryable<T>
IGrouping<TKey, TElement>
- Example:
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
- The
- Contravariance
- The
T
is contravariant in the following generic interfaces:IComparer<T>
IEqualityComparer<T>
IComparable<T>
- Example
- You have a base & a derived class
- You then create a BaseClassComparer class, implementing the
IEqualityComparer<BaseClass>
interface - Because
IEqualityComparer<T>
is contravariant you can use BaseClassComparer to run comparisons on derived classes - Syntax/example:
class BaseClass { }
class DerivedClass : BaseClass { }
// Comparer class…
class BaseComparer : IEqualityComparer<BaseClass>
{
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void InvokeContravariance()
{
IEqualityComparer<BaseClass> baseComparer =
new BaseComparer();
IEqualityComparer<DerivedClass> childComparer =
baseComparer;
}
}
- The
- Limitations on variance in generic interfaces:
- Only works for reference types (i.e., not value types)
- In other words, you cannot implicitly convert
IEnumerable<int>
toIEnumerable<obj>
, becauseint
is a value type - Also, "classes that implement variant interfaces are still invariant." (msdn.microsoft.com)
- For example,
List<T>
implements the covariant interfaceIEnumerable<T>
- Ergo, you cannot implicitly convert
List<Object>
toList<String>
- (You could, however, implicitly convert as follows:
IEnumerable<Object> listObjects = new List<String>();
)
- For example,
System.Collections.Generic
classes & the interfaces they implementDictionary<TKey, TValue>
- generic collection of keys & valuesICollection<T>
IDictionary<TKey, TValue>
IEnumerable<T>
List<T>
- dynamically resizable sequential listICollection<T>
IEnumerable<T>
IList<T>
LinkedList<T>
- a doubly-linked listICollection<T>
IEnumerable<T>
- Note: see Wikipedia for good discussions of Linked lists
- Note: see Wikipedia for good discussions of Doubly-linked lists
Queue<T>
ICollection
- i.e., non-generic interfaceIEnumerable<T>
SortedDictionary<TKey, TValue >
- generic collection of sorted keys & valuesICollection<T>
IDictionary<TKey, TValue>
IEnumerable<T>
SortedSet<T>
- ensures no duplicationICollection<T>
IEnumerable<T>
ISet<T>
Stack<T>
ICollection
- i.e., non-generic interfaceIEnumerable<T>
- Notes:
System.Collections.Generic
defines many other classes & structures- Many of these work in conjunction with above classes
- Example:
LinkedListNode<T>
is a node withinLinkedList<T>
- Libraries other than
mscorlib.dll
&System.dll
add types to the namespace
- List
- Collection initialization syntax
- Introduced in .NET 3.5
- Very much akin to Object initializer syntax
- Syntax very similar to that used to initialize arrays
- Restriction
- Can only use this approach with classes that support an
Add()
method Add()
collection is specified by theICollection/ICollection<T>
interfaces
- Can only use this approach with classes that support an
- Syntax (simple data type):
List<int> myListNos = new List<int> { 0, 1, 2, 3 };
- Syntax (blending object & collection syntax):
List<Musician> listBeatles = new List<Musician>
{
new Musician { LastNm = "Lennon", FirstNm = "John" },
new Musician { LastNm = "McCartney", FirstNm = "Paul" },
//…
}; - Effectively obviates the need to invoke
Add()
each time you add an item
- The
List<T>
class- The most commonly used generic collection
Insert()
method allows you to specify where inList<T>
you want to add a new item- There is also a
ToArray()
method:
Musicians[] Beatles = listBeatles.ToArray();
- Other methods also exist
- The
Stack<T>
class- Use
Push()
to add an item - Use
Pop()
to remove an item - Use
Peek()
to look at 'top' item - If
Stack<T>
is empty:Pop()
will throw anInvalidOperationException
- A smart programmer places
Pop
commands within aTry/Catch
block
- Use
- The
Queue<T>
classDequeue()
methodEnqueue()
methodPeek()
method
- The
SortedSet<T>
class- Introduced in .NET 4.0
- Automatically sorts items as they are added or removed
- Obviously you need to tell compiler, though, how you want items sorted
- Need to pass in an object that implements
IComparer<T>
interface - To do this:
- Create a class that implements
IComparer<T>
interface - Specify the object type you want to sort by in this declaration
- Define the class as having just one method:
public int Compare(…)
- Utilize your comparer class when new-ing the collection
- Syntax/example:
// Comparer class…
class SortPeopleByAge : IComparer<Person>
{
public int Compare(Person firstPerson, Person secondPerson)
{
if (firstPerson.Age > secondPerson.Age)
return 1;
if (firstPerson.Age < secondPerson.Age)
return -1;
else
return 0;
}
}
// Create sorted set, including your comparer class as a
// constructor argument when new-ing your person class…
SortedSet<Person> setOfBluths =
new SortedSet<Person>(new SortPeopleByAge())
{
new Person { FirstName="George", LastName="Bluth", Age=65 },
new Person { FirstName="Michael", LastName="Bluth", Age=40 },
};
// Add more people…
setOfBluths.Add(new Person { FirstName = "George Michael",
LastName = "Bluth", Age = 14 });
setOfBluths.Add(new Person { FirstName = "Maeby",
LastName = "Fünke", Age = 14 });
- Create a class that implements
- Need to pass in an object that implements
- The
Dictionary<TKey, TValue>
class- A lot like the
List<T>
collection - However, with
Dictionary<TKey, TValue>
you retrieve items by a key value (e.g.,string
name) rather than by a numerical position - Note: you will get a runtime error if your
TKey
values are not unique - To populate
- Can, of course, use the
Add()
method - When adding your
TValue
items can use object initialization syntax - Can also use initialization syntax to obviate repeated invocations of
Add()
- Can, of course, use the
- Dictionary initialization syntax
- New as of C# 6.0
- Borrows from indexer syntax
- Syntax/example:
Dictionary<string, Bluth> someBluths = new Dictionary<string, Bluth>()
{
["George"] = new Bluth { fName = "George",
lName = "Bluth", age = 67 },
["Lucille"] = new Bluth { fName = "Lucille",
lName = "Bluth", age = 65 },
["Lindsey"] = new Bluth { fName = "Lindsey",
lName = "Bluth Fünke", age = 35 }
};
- A lot like the
- General
System.Collections.ObjectModel
namespace- Two main classes
ObservableCollection<T>
ReadOnlyObservableCollection<T>
- Both classes can notify external classes when their contents change
- Working with
ObservableCollection<T>
- Implements the same interfaces as
List<T>
- What differentiates
ObservableCollection<T>
is that it supports an event namedCollectionChanged
CollectionChanged
is fired whenever- A new item is added
- A current item is moved or deleted
- The entire collection is changed
- Underlying delegate is
NotifyCollectionChangedEventHandler
, which takes 2 parameters- 1st:
object
- 2nd:
NotifyCollectionChangedEventArgs
- 1st:
- Syntax/example - delegate handling
class Program
{
static void Main(string[] args)
{
// Make a collection to observe & add a few Person objects…
ObservableCollection<Person> people =
new ObservableCollection<Person>()
{ // Add Persons via object initialization syntax… };
// Define event handle…
people.CollectionChanged += People_CollectionChanged;
// Add & remove Persons to/from collection…
}
}
private static void People_CollectionChanged(object sender,
System.Collections.Specialized.
NotifyCollectionChangedEventArgs e)
{
// What raised the event?…
WriteLine("Action for this event: {0}", e.Action);
// Handle items removed & added…
if (e.Action == System.Collections.Specialized.
NotifyCollectionChangedAction.Remove)
{
foreach (Person p in e.OldItems)
WriteLine(p.ToString());
}
if (e.Action== System.Collections.
Specialized.NotifyCollectionChangedAction.Add)
{
foreach (Person p in e.NewItems)
WriteLine(p.ToString());
}
}
} - Notes re: looping through
OldItems
&NewItems
collection properties- Be sure to wrap any
foreach
loops inside the appropriateAction
enumeration (as was done above) - The
NotifyCollectionChangedAction
is anenum
with 5 values:Add
Remove
Replace
Move
Reset
- Be sure to wrap any
- Implements the same interfaces as
- Two main classes
- Creating custom generic methods
- General
- Generally, base class libraries work just fine
- Custom generics have effect of creating super overloading
- Example: Without generics if you need, say,
Swap()
methods, you need to create essentially identical code for each type you might want to swap - Could somewhat alleviate this by defining a method that takes an
object
parameter, but that gets back to all issues which drove creation of generics in first place (casting, type safety, etc.) - Look to create custom generics "Whenever you have a group of overloaded methods that only differ by incoming arguments…" (Troelsen & Japikse: 7th Ed., p. 344)
- Syntax of custom generic methods is to insert a type parameter between the method name & its parameter list
- Additionally, when creating custom generic methods you can employ multiple parameters - e.g.:
static void Swap<T>(ref T a, ref T b)
{ // implementation code… }
- Inference of type parameters
- If you have a generic method that takes parameters you have the option of omitting the type declaration when invoking the method
- Compiler will infer the correct type for you
- Example (using full declaration syntax):
Swap<int>(ref T a, ref T b);
- Example (using full declaration syntax):
int a = 1, b = 2;
Swap<int>(ref a, ref b); - Example (using type inference):
int a = 1, b = 2;
Swap(ref a, ref b); - Troelsen & Japikse (7th Ed., p. 346), however, suggest that leaning on type inference can lead to tears
- General
- Creating custom generic structures & classes
- General
- Syntax:
Public Class MyClass<T>
- Syntax for structure is the same
- Can then use generic type parameter in constructors, properties, methods, etc.
- Syntax/example - Generic
struct
public struct Point<T>
{
// Generic state data…
private T xPos;
private T yPos;
//Generic ctor…
Point(T xVal, T yVal)
{
xPos = xVal;
yPos = yVal;
}
// Ppts (Y ppt is the same)…
public T X
{
get { return xPos; }
set { xPos = value; }
}
// Reset fields to their default value of type param (see next)…
public void Reset()
{
xPos = default(T);
yPos = default(T);
}
}
- Syntax:
- The
default
keyword in generic code- May want to (re)set a field to the default property of the generic type
- Syntax:
field1 = default(T);
- The
default
keyword, of course, is also used inswitch
statements - Defaults are:
- Numeric values:
0
- Reference types:
null
- Fields of a structure:
0
for value types &null
for reference types
- Numeric values:
- Generic base classes
- Generic classes can be base classes to other classes
- Ergo, generic classes can define virtual & abstract methods
- Restrictions
- Derived class must specify the type parameter of the base class
- Example:
Public Class DerivedClass : BaseClass<string>
- If the base class contains generic virtual or abstract methods the derived class must use the specified type parameter when overriding those methods
- If derived based class is also generic it:
- May reuse the type placeholder in its definition
- Must honor all constraints on the base class
- General
- Constraining type parameters
- General
- While generics are type-safe, you can further tighten up your code by using the
where
keyword - You can use more than one
where
constraint - Options
where T : struct
- i.e., the type parameter must haveSystem.ValueType
in its lineagewhere T : class
- The type parameter must not have
System.ValueType
in its lineage - In other words, it must be a reference type
- The type parameter must not have
where T : new()
- The type parameter must have default constructor
- Helpful if your generic type must create an instance of type parameter (after all, can never know custom constructors)
- If using 1+
where
clauses this must be listed last
where T : NameOfBaseClase
- i.e., the type parameter must haveNameOfBaseClass
in its lineagewhere T : NameOfInterface
- i.e., the type parameter must implement the specified interface(s)
- While generics are type-safe, you can further tighten up your code by using the
- Using the
where
keywordwhere T : new()
& compile-time error checking- Default constructor is removed whenever you create a custom constructor
- Will find if programmer remembered to recreate default constructor
- The
where
keyword specifies which parameter is being constrained (as you could also haveTKey
orTValue
parameters) - Example of using multiple constraints:
public class MyClass<T> where T : class, IDrawable, new()
- Example (multiple constraints on multiple parameters):
public class MyClass<T, K> where T : struct, IComparable<T>
where K : SomeBaseClass, new()
{ // Implementation code… } - You can also use
where
keyword on generic methods - Derived classes
- Derived types must honor constraints
- Example:
public class MyBaseClass<T> where T : new()
{ //Implementation code… }
public class MyDerivedClass<T> : MyBaseClass<T> where T : new()
{ //Implementation code… }
- Lack of operator constraints
- You cannot apply any C# operators (e.g.,
+, -, *
, etc.) on type parameters out-of-the-box - This is true even if you have constrained your parameter to be a
struct
- If you really need to use operators on generic parameters you could do so via interfaces, as interfaces can define operators!
- You cannot apply any C# operators (e.g.,
- General
- General
- General
- Delegates, Events & Lambdas
- General
- "A delegate is a type that safely encapsulates a method. " docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Delegates > Using Delegates
- "Delegates are used to pass methods as arguments to other methods." docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Delegates
- Delegate types
- Type-safe objects
- "Point to" a method (or list of methods) that can be invoked later
- Built-in support for:
- Multicasting
- Asynchronous method invocation
- Delegates
- Basics & definitions
- The .NET
Delegate
type- Unlike traditional C-style callbacks (created via function pointers), .NET delegates are configured to specify:
- Number & types of parameters
- Return type (if any) of pointed-to method
- With the encapsulation & abstraction provided by delegates, delegates are very similar to interfaces' representation of classes
- Can be set up to invoke/call its target method(s) based on conditions at runtime
- Can point to either static or instance methods
- Two ways of invoking methods:
- Synchronously
- Asynchronously - & can do so without having to deal with
Thread
object
- Unlike traditional C-style callbacks (created via function pointers), .NET delegates are configured to specify:
- Common uses for delegates
- As dicussed below in Sending object state notifications using delegates, delegates are often used to get information about the state of an object
- Callbacks
- Sometimes in code you need an object that can "call you back" - i.e., you send the object a message & the object sends a message back to you
- Most frequently see callback mechanisms with graphical user interfaces (e.g., buttons)
- "Under the .NET platform, the delegate type is the preferred means of defining and responding to callbacks within applications." (Troelsen & Japikse: 7th Ed., p. 355)
- Notifying a caller when a long process has completed (i.e., asynchonous callbacks)
- Passing one method into another
- This can be very powerful
- Syntax/example:
class Program
{
// Define delegate…
delegate void MyDel(string message);
static void Main(string[] args)
{
MyDel handler = HandleStringMsg;
// Invoke method, passing in MyDel instance…
UsingMyDel(1, 2, handler);
}
static void HandleStringMsg(string message)
{ Console.WriteLine(message); }
static void UsingMyDel(int x, int y, MyDel callback)
{
callback(string.Format("Sum of {0} + {1} =
{2}", x, y, (x + y)));
}
}
- Defining a custom comparison method
- Encapsulate the method in a delegate
- Then pass that method into a sorting algorithm
- Defining a
Delegate
type in C#- Utilize
delegate
keyword- Even though
Delegate
is one of the 5 main .NET types, you do not- Create a stand-along file for it, as you do with classes & interfaces
- Even employ scope brackets to define a delegate
- Example (note simplicity):
public delegate int BinaryOp(int x, int y);
- Must match signature of method will point to
- This delegate can point to any method which returns an
int
& takes 2ints
as parameters
- Even though
- How C# creates a delegate class 'behind the scenes'
- Creates a sealed class deriving from
System.MulticastDelegate
(which in turn derives fromSystem.Delegate
) - Note: though delegates inherit from
System.MulticastDelegate
(&, therefore, in turn, fromSystem.Delegate
) you will receive a compiler error if you try to invoke this inheritance explicitly - 3 methods in class:
Invoke()
- Calls method synchronously - i.e., called method must finish before calling method can continue
- Though you may explicitly call
Invoke()
there is no need to do so (seen below)
BeginInvoke()
&EndInvoke()
- Both commands are used for asynchronous method invocation - i.e., on separate thread of execution
- Obviates the need to work with
Thread
object
- How compiler handles the 3 methods
Invoke()
- signature exactly matches the signature of the delegateBeginInvoke()
- Return type:
IAsyncResult
- 1st parameters: parameters - & all parameter modifiers - of the delegate
- Additional parameters:
AsyncCallback
type, &object
type to capture state
- Return type:
EndInvoke()
- Return type: the return type, if any, of the delegate
- Parameter(s): an
IAsyncResult
type
- Example:
sealed class BinaryOp : System.MuticastDelegate
{
public int Invoke(int x, int y);
public IAsyncResult BeginInvoke(int x, int y,
AsyncCallback cb, object state);
public int EndInvoke(IAsyncResult result);
}
out, ref,
¶ms
parameters- These can all be handled
Invoke()
&BeginInvoke()
methods operate just as described aboveEndInvoke()
, however, changes- Any & all
out
&ref
parameters of delegate are now included (along with theIAsyncResult
type) - Return type remains the type returned by the delegate
- Any & all
- Creates a sealed class deriving from
- Utilize
- Delegates, pointed-to methods, & objects
- While a delegate points to a method, if the method is an instance method the delegate also 'has knowledge' of the instance
- That said, all the delegate 'knows' about the
object
is that it contains a method whose signature matches that of the delegate - As such, the delegate is otherwise agnostic about the method-containing object
- "When a delegate is constructed to wrap a static method, it only references the method." docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Delegates > Using Delegates
- The
System.MulticastDelegate
&System.Delegate
base classesSystem.MulticastDelegate
methods, operators, & fields (partial list)- A delegate inherits from this only when it points to multiple methods
public sealed override Delegate[] GetInvocationList()
- returns list of methods 'pointed to'- Overloaded operators
public static bool operator ==(MuticastDelegate d1, MulticastDelegate d2);
public static bool operator !=(MuticastDelegate d1, MulticastDelegate d2);
- Internal fields
private IntPtr _invocationCount;
private object _invocationList;
- Notes on
IntPtr
type- Typically used to represent a pointer or handle
- Implements
ISerializable
- Designed to be an integer
- Size is platform specific - i.e., 32-bits on 32-bit hardware, 64-bits on 64-bit hardware
- See docs.microsoft.com > .NET > .NET Framework API Reference > .NET API Browser > System [Namespace] > IntPtr for full details
System.Delegate
methods, operators, & properties (partial list)- Note:
System.Delegate
class implementsICloneable, ISerializable
interfaces - Methods (use to handle list of functions):
public static Delegate Combine(params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);
public static Delegate Remove(Delegate source, Delegate value);
public static Delegate RemoveAll(Delegate source, Delegate value);
- Overloaded operators
public static bool operator ==(Delegate d1, Delegate d2);
public static bool operator !=(Delegate d1, Delegate d2);
- Properties that return the delegate target:
public MethodInfo Method { get; }
public object Target { get; }
- Note:
- Core members:
Method
property- Returns a
System.Reflection.MethodInfo
object - Gives you info about static method maintained by delegate
- Returns a
Target
property- "…returns an object that represents the method maintained by the delegate." (Troelsen & Japikse: 7th Ed., Table 10‑1, p. 360)
- If this property returns
null
, however, the method to be called is a static member
Combine()
- Adds a method to the delegate list
- In C# invoke via the (overloaded)
+=
operator
GetInvocationList()
- Returns an array of
System.Delegate
objects - Each such object represents a method which you can invoke
- Returns an array of
Remove()
&RemoveAll()
- Static methods
- In C# can use overloaded
-=
operator in lieu ofRemove()
- Instantiating & using a
Delegate
- Must include a/the pointed-to method when instantiating a delegate
- Syntax/example:
class Program
{
static void Main(string[] args)
{
// Create delegate type…
public delegate int BinaryOp(int x, int y);
// Instantiate delegate…
BinaryOp addInts = AddTwoInts;
// Alternate instantiation syntax…
BinaryOp addInts = new BinaryOp(AddTwoInts);
}
// Pointed-to method…
static int AddTwoInts(int x, int y)
{ return x + y; }
} - (Discussed below is another option, C# anonymous methods)
- After declaring a delegate instance & having it 'point to' a method you simply use the name of the delegate variable in lieu of the name of the pointed-to method
- Example (using the above BinaryOp example):
b(1,2);
, whereb
is aBinaryOp
instance (obviously pointing to a method which takes 2ints
& returns anint
) - You also have the option of explicitly calling
Invoke()
- e.g..,b.Invoke(1,2);
(takes 2ints
& returns anint
)
- Multicasting
- Again, this is where a delegate calls > 1 method when invoked
- Used extensively in event handling
- Methods will be called in the order in which they were attached to the delegate instance
- Reference parameters
- Passed from one method to next
- A change to the value of a referenced parameter is visible to ensuing methods
- Unhandled exceptions
- Passed to the delegate caller
- No further methods are invoked
- Caller will receive the return value and/or out parameter of the last method invoked
- Reference parameters
- Syntax/example:
class Program
{
// Define delegate…
delegate void MyDel(string message);
static void Main(string[] args)
{
// Define delegate, class variables…
MyDel handler = HandleStringMsg;
SomeClass obj = new SomeClass();
// Define delegate variables…
MyDel d1 = obj.StrHandler1;
MyDel d2 = obj.StrHandler2;
MyDel d3 = HandleStringMsg;
// Enable multicasting…
MyDel multiDel = d1 + d2;
multiDel += d3;
multiDel -= d1; // …remove a method
// Get fancy…
MyDel oneMethodDel = multiDel - d2;
}
static void HandleStringMsg(string message)
{ Console.WriteLine(message); }
}
class SomeClass
{
internal void StrHandler1(string message) { }
internal void StrHandler2(string message) { }
}
- The .NET
- Sending object state notifications using delegates
- General
- Notifying callers of object state is a big reason for using delegates
- To do this you typically:
- Define a public delegate type
- Declaration can occur either as a:
- Public property of the delegate-invoking class
- Public declaration outside the class but inside the relevant namespace
- This delegate will be used to point to methods outside the object - i.e., to send state notifications
- Effectively an event handler
- Declaration can occur either as a:
- Declare a (private) member variable of this delegate type inside your class
- This will hold list of methods to be invoked under various circumstances
- Downside of
private
scope: must create a register-with-delegate method (i.e., rather than simply allowing caller to attach methods) - Upside of
private
scope: better adherence to encapsulation (& type safety)
- Create a (public) helper function in the object that allows a caller to attach/register an external method to/with the delegate object
- Function should take the delegate defined within the class as its sole parameter
- This is why the delegate type must be declared as public
- To do this the argument should be
(new yourObjInstance.PublicDelegateProperty(nameOfExternalMethod))
- Remember to check that private variable
!= null
before attaching a function to it!
- Syntax/example:
class MyClass
{
// PUBLIC ppty of delegate type…
public delegate void MyHandler(string msgForCaller);
// Private delegate instance …
private MyHandler listOfHandlers;
// Registration function for callers…
public void RegisterWithMyHandler(
MyHandler methodToCall)
{
if (listOfHandlers == null)
{
listOfHandlers = new MyHandler(methodToCall);
}
else
{
listOfHandlers += methodToCall;
}
}
} - Syntax/example (registering a method with your object):
class Program
{
static void Main(string[] args)
{
// New instance of your class…
MyClass mc = new MyClass();
// Register the method to call…
mc.RegisterWithMyHandler(
new myClass.MyHandler(MethodToCall));
}
// Define the event-capturing method…
public static void MethodToCall(string msg)
{ // Implementation code… }
}
- Define a public delegate type
- See Troelsen & Japikse: 7th Ed., pp. 364‑6, for full sample code
- Enabling multicasting
- To add additional methods to a delegate continue to make use of
+=
operator - Can alternatively make use of static
Delegate.Combine()
method
- To add additional methods to a delegate continue to make use of
- Removing targets form a delegate's invocation list
- There is a static
Delegate.Remove()
method - As a shortcut can make use of
-=
operator - Typically you create a public helper method for removing a method, just as you create one for adding a method
- There is a static
- General
- Method group conversion syntax
- Comes into play regarding the event-registration method you create in a custom object
- Background:
- You create a (public) property of your
delegate
type - You then pass a
new
(external) instance of that delegate type into your registration method
- You create a (public) property of your
- Problem:
- You have created a delegate object simply for the purpose of telling your object's event-handler which method to point to
- You then have an object (i.e., the delegate created in your object-calling code) 'kicking around' after its one-time use
- Solution (i.e., method group conversion syntax):
- In point of fact, you really are only interested in supplying the name of the method(s) to be attached (or detached)
- In cases where a method takes a delegate as parameter(s) method group conversion syntax means you need only supply a (pointed-to) method name
- Syntax/example:
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
// Register with simple method name…
mc.RegisterWithMyClass(SendMsgHere);
}
static void SendMsgHere(string msg)
{ // Handle string msg… }
}
- Delegate covariance
- Relates to cases where a delegate's pointed-to method returns a custom class type
- Specifically:
- Assume you have 2 custom class types:
class Parent {//Implementation code… }
class Child : Parent {//Implementation code… }
- You have two methods:
public static ObjParent MethodP();
public static ObjChild MethodC();
- Assume you have 2 custom class types:
- Prior to .NET 2.0 if you wanted a delegate which pointed to
MethodC
it had to be a different delegate from one pointing toMethodP
- Delegate Covariance (a.k.a. relaxed delegates), however, allows you to create one delegate pointing to both methods in our example
- Approach
- Create a delegate type which points to method returning the parent object
- Create an instance of this delegate, but pass in a method which returns the child type
- Lastly, when utilizing the delegate instance cast the returned object to the child type
- Example/Syntax (creating a delegate instance which points to child object):
// Create delegate type…
delegate ObjParent ObtainParentTypeDelegate();
// Create a method returning a child type…
static ObjChild GetChildType()
{ return new ObjChild(); }
// Use covariance to return ObjChild…
ObtainParentTypeDelegate handlerParent = GetChildType;
// Cast the result…
ObjChild child = (ObjChild)handlerParent(); - Notes:
- Troelsen's sample code shows this final cast (Troelsen & Japikse: 7th Ed., p. 415)
- Microsoft Docs, however, indicates a final cast is not necessary (see docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > Covariance and Contravariance > Variance in Delegates)
- Delegate contravariance
- Contravariance allows you to create one delegate pointing to multiple methods where the methods receive objects related by classical inheritance
- Syntax/example:
// Event hander that accepts a parameter of the EventArgs type…
private void MultiHandler(object sender, System.EventArgs e)
{ label1.Text = System.DateTime.Now.ToString(); }
public Form1()
{
InitializeComponent();
// You can use a method that has an EventArgs parameter,
// although the event expects the KeyEventArgs parameter…
this.button1.KeyDown += this.MultiHandler;
// You can use the same method
// for an event that expects the MouseEventArgs parameter…
this.button1.MouseClick += this.MultiHandler;
}
- Note: Example is from docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > Covariance and Contravariance > Variance in Delegates > Using&nsbp;Variance in Delegates
- Generic delegates
- General/Example
- You have 2 methods which each return
void
& take a single parameter - The parameter for one method is a
string
, & for the 2nd it is anint
- You can declare a single (generic) delegate type to handle both methods
- Your instances of that (generic) delegate type are then configured appropriately
- Example:
//Class-level delegate type…
public delegate void MyGenericDel<T>(T arg);
//Create delegate instance to handle a 'string' method…
MyGenericDel<string> strDel =
new MyGenericDel<string>(StringMethod);
//Create delegate instance to handle an 'int' method…
MyGenericDel<int> intDel =
new MyGenericDel<int>(IntMethod);
- You have 2 methods which each return
- Simulating generic delegates without generics
- Prior to .NET 2.0 you achieved the 'same' result by defining a delegate which took an
object
parameter - Example:
public delegate void MyNonGenericDel(object arg);
- Pointed-to method would then (of course) have to take an
object
as its parameter - Further, guts of this method would have to:
- Test the
arg
parameter to see its type - Cast the object to the appropriate type
- Use an
if
orswitch
statement to branch to the appropriate operating code
- Test the
- As was the case with much pre-.NET 2.0 code, the drawbacks were:
- Lack of type safety
- Boxing/unboxing penalties
- Prior to .NET 2.0 you achieved the 'same' result by defining a delegate which took an
- The generic
Action<>
&Func<>
delegates- Problem:
- As described so far when using a delegate for callbacks you
- Define a custom delegate
- Create an instance of that delegate (passing in the pointed-to method name as a constructor parameter)
- Invoke the method via your custom delegate
- In sum, you are creating a custom delegate that you are using for a very narrow application
- While having a uniquely-named delegate is nice, typically all you really need is a delegate that will point to the desired method
- As described so far when using a delegate for callbacks you
- Solution:
- The generic
Action<>
delegate- Defined in the
System
namespace (in bothmscorlib.dll
&System.Core.dll
assemblies) - Can use whenever you are pointing to a method which returns
void
- Delegate takes up to 16 parameters
- Note: because
Action<>
is a generic delegate you need to specify the type of each parameter - Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 372‑3):
class Program
{
static void Main(string[] args)
{
// Use the Action<> delegate to point to DisplayMessage…
Action<string, ConsoleColor, int> actionTarget =
new Action<string, ConsoleColor, int>(DisplayMessage);
actionTarget("Action Message!", ConsoleColor.Yellow, 5);
Console.ReadLine();
}
static void DisplayMessage(string msg,
ConsoleColor txtColor, int printCount)
{
// Code to set color of the console text,
// print msg printCount nmbr times…
}
} - Note: method group conversion syntax allows you to simplify above delegate declaration as follows:
Action<string, ConsoleColor, int> actionTarget = DisplayMessage;
- Defined in the
- The generic
Func<>
delegate- Use
Func<>
when you need to point to a method which returns a value - Like
Action<>
,Func<>
can point to methods taking up to 16 parameters - Set the final parameter (17th??) of
Func<>
equal to the type returned by the pointed-to method
- Use
- The generic
- Problem:
- General/Example
- Basics & definitions
- C# events
- Limitations with delegates
- Fair amount of boilerplate code
- Creating/defining the delegate
- Creating a private instance of the delegate
- Creating a registration method
- Risk: failure to declare delegate member variables as
private
- Obviously caller would then have access to the delegate variable
- Caller could:
- Remove pointed-to methods from the delegate variable
- Redefine the delegate variable entirely
- Directly invoke pointed-to methods
- Typically you only want to invoke pointed-to methods under designed circumstances (i.e., within
if
blocks) - With an inadvertently-public delegate declaration caller can invoke those methods willy-nilly
- Typically you only want to invoke pointed-to methods under designed circumstances (i.e., within
- See Troelsen & Japikse: 7th Ed., pp. 374‑5, for example of 'hijacking' a
public
delegate instance
- Fair amount of boilerplate code
- The C#
event
keyword- Basically, it just provides some syntactical shortcuts
Event
keyword obviates need to create registration & unregistration methods- Compiler automatically creates registration & unregistration methods
- Also establishes private delegate member variables
- 2 basic steps in creating:
- 1) Declare the appropriate (public) delegate type
- (This delegate will, of course, hold the list of methods to be fired by the event)
- 2) Declare (public)
events
corresponding to the appropriate delegate type - Syntax/example:
public class MyClass
{
// Declare delegate to work with events…
public delegate void MyClassSimpleEventHandler(string msg);
// See "Creating custom event arguments"
// below for this syntax…
public delegate void MyClassFullEventHandler(
object sender, MyClassEventArgs e);
// Declare events…
public event MyClassSimpleEventHandler Event1;
public event MyClassFullEventHandler Event2;
}
- Notifying caller(s) than an
event
has occurred- Typically you fire an event inside the scope of an
if
statement- The
if
statement generally is testing whether some property value has been met or exceeded - Firing the event simply consists of supplying the called-for parameters (as specified by the delegate defining the event) against the
Event
variable - Note: before firing an event remember to test the event instance against a
null
value (this confirms caller has registered a method with the event)
- The
- Syntax/example (adapted from car example in Troelsen & Japikse: 7th Ed., pp. 376‑7):
class Car
{
// Field & ppty declarations omitted…
// CarEventArgs defined as a separate class, see below…
public delegate void CarEngineHandler(
object sender, CarEventArgs e);
// Declare events…
public event CarEngineHandler Exploded;
public event CarEngineHandler AboutToBlow;
public void Accelerate(int delta)
{
if (carIsDead) //…boolean field
{
if (Exploded != null)
Exploded(this, new CarEventArgs("Car is dead…"));
}
else
{
CurrentSpeed += delta;
// Too fast…
if (10 == (MaxSpeed - CurrentSpeed) &&
AboutToBlow != null)
{
AboutToBlow(this, new CarEventArgs("Slow down!"));
}
// Handle other cases…
}
}
}
- Typically you fire an event inside the scope of an
- Basically, it just provides some syntactical shortcuts
- Events under the hood
- When creating an event CIL adds 2 hidden methods
- The 2 methods are named by adding
add_
&remove_
prefixes to the name of the event (e.g.,add_myEvent, remove_myEvent
) - These methods obviously take the place of the registration methods you would create by hand if not using events
- Notes:
add_
method makes call to delegate'sCombine()
methodremove_
method makes call to delegate'sRemove()
method
- CIL also makes use of
.addon
&.removeon
directives
- Listening to incoming events
- Last piece is handling how you register caller-side event-handlers with events
- Instead of creating handler-registration methods you simply utilize
+=
&-=
operators - Syntax 1 (verbose):
YourObject.RelatedDelegate d =
new YourObject.RelatedDelegate(MethodToCall);
YourObject.YourEvent += d; - Syntax 2 (terser):
YourObject.YourEvent += new RelatedDelegate(MethodToCall);
- Syntax 3 (using method group conversion syntax - i.e, pithiest):
YourObject.YourEvent += MethodToCall;
- Note: these operators trigger the appropriate
add_XXX
orremove_XXX
inside the CIL
- Event registration using VS
- IntelliSense window pops up whenever you type
+=
syntax to register an event - Use key to complete code
- Will generate the desired method for you
- Employs method group conversion syntax
- IntelliSense window pops up whenever you type
- Cleaning up event invocation using the C# 6.0 null-conditional operator (i.e.,
?
)- So far one of the clunky aspects of events is the need to wrap event-firing in a
if (myEvent != null)
statement - Again, this is done to ensure that a caller has registered at least one method with the event (delegate)
- With C# 6.0 can now utilize null-conditional operator instead
- Syntax/example (from above car example):
// Original event-firing when car is dead…
if (carIsDead)
{
if (Exploded != null)
Exploded("Car is dead…");
}
// Revised event firing…
if (carIsDead)
Exploded?.Invoke("This car is dead…"); - Note: as seen in this example, when using the null-conditional operator you must explicitly utilize the delegate's
Invoke
method
- So far one of the clunky aspects of events is the need to wrap event-firing in a
- Creating custom event arguments
- MSFT has a recommended event pattern, which is adhered to by all delegates/events in the base class libraries
- Specifically, MSFT recommends that your event handlers be built with these 2 parameters (in order):
System.Object
- to pass along a reference to the object firing the event- The
System.Object
reference is usually calledsender
- Pass in the
this
keyword to establish the reference
- The
- A descendent of
System.EventArgs
(usually callede
)- Used to include information about the event
- You
new
an instance of this class within the event-firing code
System.EventArgs
base class:
public class EventArgs
{
public static readonly System.EventArgs Empty;
public EventArgs();
}- If you do not have any custom information to send you simply pass an instance of
EventArgs
- You accomplish this by passing in
EventArgs.Empty
as your second parameter - This works because the
Empty
property ofEventArgs
is (cleverly!) of typeEventArgs
- If you do not have any custom information to send you simply pass an instance of
- If you do need to send information about the event
- Create a public class inheriting from
EventArgs
- Use an instance of that class as the 2nd argument in your external event handler
- Syntax/Example:
public class MyClassEventArgs : EventArgs
{
public readonly string msg;
public MyClassEventArgs(string message) // …ctor
{ msg = message; }
} - Note: .NET practice is to append
EventArgs
to the name of your custom class
- Create a public class inheriting from
- With the event handler:
- Cast the received
object
(i.e.,sender
) to the appropriate class type - Best Practice: wrap the cast inside a
if (sender is YourClass)
trap - Note, however: event handlers do not have to include the
sender
&e
types as parameters!- Event handlers also have the option of including only 1 of the 2 types as parameters
- This is obviously an exception to the requirement that delegates have signatures which match their pointed-to functions
- Cast the received
- See Troelsen & Japikse: 7th Ed., pp. 382‑3, for a full example
- The
EventHandler
& genericEventHandler<T>
delegates- General
- Using either of these saves you from having to define a custom delegate
- These delegates can only point to methods which return
void
- Typically, though, event handlers do not have return values
EventHandler
delegate- Use this when you are not sending data with your event (i.e., you are not creating a custom
EventArgs
class) - Definition:
[SerializableAttribute]
[ComVisibleAttribute(true)]
public delegate void EventHandler(
object sender,
EventArgs e
) - You must pass in
EventArgs.Empty
as the 2nd parameter when invoking this delegate
- Use this when you are not sending data with your event (i.e., you are not creating a custom
EventHandler<T>
generic delegate- Use this when you:
- Are sending data with your event
- Have defined a custom
EventArgs
-derived class for that purpose
- Definition:
public delegate void EventHandler<TEventArgs>(
object sender,
TEventArgs e
) - You then use this generic delegate to define your event (again, obviating the need to define a custom delegate)
- Syntax/example (assumes we have defined
CustomEventArgs
in/as a separate (public) class):
MyClass
{
// Define ppts whose values we want to watch via events…
// Employ generic delegate to define event…
public event EventHandler<CustomEventArgs> PptyValueReached;
public void ChangePptyValue()
{
if (// ppty value excedes limit…)
{
CustomEventArgs args = new CustomEventArgs();
// Set appropriate values of args variable…
OnPptyLimitReached(args);
}
}
protected virtual void OnPptyLimitReached(CustomEventArgs e)
{
// Define (generic) delegate variable, instantiate as event…
EventHandler<CustomEventArgs> handler =
PptyValueReached;
handler?.Invoke(this,e);
}
} - Note: make event-firing methods
protected virtual
so that derived classes can change - Syntax/example - handling
EventHandling<T>
delegate on calling side:
static void SomeMethod()
{
// Define instance of class…
MyClass mc = new MyClass();
// Register event-handler…
EventHandler<CustomEventArgs> d =
new EventHandler<CustomEventArgs>(EventHandlingMethod);
mc.PptyValueReached += d;
}
EventHandlingMethod(object sender, CustomEventArgs e)
{ // Event-handling code… }
- Use this when you:
- General
- Limitations with delegates
- C# anonymous methods
- General
- As things stand event-handling code is still a little verbose
- Typically, the event-handling code is not called by other areas of your project
- However, you have still built a full-blown method for that very limited purpose
- To handle this wee bit of inefficiency .NET provides anonymous methods:
- Immediately following your event-registration line include, in braces, your event-handling code
- Move the semicolon from the end of the event-registration line & place it after the code-closing brace
- "Creating anonymous methods is essentially a way to pass a code block as a delegate parameter." (docs.microsoft.com > C# Guide > Programming guide > Statements, Expressions, and Operators > Anonymous Functions > Anonymous Methods)
- Anonymous methods therefore eliminate the need to create event-handling methods
- Syntax/Example 1:
static void SomeMethod()
{
MyType t = new MyType();
// NO semi-colon!…
t.SomeEvent += delegate(object sender, CustomEventArgs e)
{ /* event-handling code */ }; // NOTE closing semi-colon…
} - Syntax/example 2:
static void SomeMethod()
{
// Create a delegate…
delegate void StringHandler(string msg);
// Instantiate the delegate using an anonymous method…
StringHandler sh = delegate(string s) { /* … */ };
}
- As things stand event-handling code is still a little verbose
- Accessing local variables
- Terminology
- All of the code inside the brackets defining an anonymous method is the anonymous-method-block
- Variables outside the anonymous-method-block but inside the method defining the anonymous method are outer variables
- Jump statements (e.g.,
goto, break, continue
) are not allowed to direct code to a target…- Outside the anonymous method block from inside that block
- Into an anonymous method block from outside it
- Capturing an outer variable
- Anonymous methods are unusual in that they are able to access the local variables of their defining methods (e.g., in the above example any variables defined within
SomeMethod()
) - Capturing an outer variable extends that variable's lifetime
- Specifically, the variable will live until all of the delegates referencing the anonymous method are eligible for garbage collection
- Anonymous methods are unusual in that they are able to access the local variables of their defining methods (e.g., in the above example any variables defined within
- Rules/limitations for/to anonymous methods & variables
- Cannot access outer
ref
orout
variables - In other words, outer variables which can be accessed are either/both instance & static variables
- Name clashes
- An anonymous method cannot have its own local variable with the same name as an outer local variable
- An anonymous method can, however, declare local variables with same name as outer class member variables
- Note: in this 2nd circumstance the anonymous method's local variable has its own scope & hides the outer class member variable
- Cannot access outer
- Terminology
- Anonymous methods vs. Lambda expressions
- Anonymous methods, introduced with .NET 2.0, effectively supplanted by lambda expressions, introduced with .NET 3.0
- The one (small) advantage anonymous methods have vs. lambda expressions is that you wind up omitting the parameter list
- General
- Expression trees
- General
- Expression trees are a tree-like representation of expressions in your code
- Each node in the tree is an element of your expression
- Expression trees can be compiled into good old CIL
- Uses
- Allows you to make run-time changes to your underlying code
- Can be the preferred method for composing LINQ queries against certain databases
- Using the Dynamic Language Runtime allows you to compile expressions into languages other than the CIL
- The expression tree API
- Found in the
System.Linq.Expressions
namespace - The (abstract)
Expression
class- Parent for approx 2 dozen classes used to build an expression tree
BinaryExpression
ConditionalExpression
ConstantExpression
GotoExpression
LabelExpression
LambdaExpression
LoopExpression
MethodCallExpression
ParameterExpression
- Others
- Methods
- An extensive number of methods
- Many are overloaded multiple times
- Used to build your final expression tree
- Parent for approx 2 dozen classes used to build an expression tree
- You always work with delegates when building expression trees
- See Docs.Microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > Expression Trees for full documentation
- Found in the
- General
- Lambda expressions
- General
- "Lambda expressions are nothing more than a very concise way to author anonymous methods…" (Troelsen & Japikse: 7th Ed., p. 388)
- Syntax/example:
delegate int myDel(int i);
static void Main(string[] args)
{
myDel md = i => i * i;
int j = md(5); // … j == 25
}
- Lambdas are also used to create expression trees
- Syntax/example:
using System.Linq.Expressions;
namespace MyETLambda
{
class Program
{
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
}
}
} - Lambdas particularly useful in LINQ expressions (typically in
Where
clauses) - All restrictions that apply to anonymous methods also apply to lambda expressions
- Dissecting a lambda expression
- List of parameters precede the
=>
token- These parameters can be implicitly or explicitly typed
- Note: if you explicitly identify the data type of one parameter then all parameters in the lambda expression must be handled similarly
- When explicitly typing parameters put the variable definition statement inside parentheses
- Example:
(int x, string s) => s.Length > x
- Putting parameters in parentheses
- Optional with a single parameter
- Manadatory with multiple parameters
- For a delegate which takes no parameters use empty parentheses
- Syntax/examples:
// Single parameter lambda…
x => x * x
// Multiple parameter lambda…
(x, y) => x + y
// Zero parameter lambda…
() => Console.WriteLine("Hello, World!")
- Statements which will process the parameters follow the
=>
token - If desired, it is perfectly acceptable to wrap the entire lambda calculus in parentheses
- List of parameters precede the
- Expression lambdas
- These are lambdas which have an expression on the right side of the
=>
operator - Used extensively in constructing expression trees
- Method calls
- Can be employed
- Example:
() => MyMethod()
- However…
- A method call has no meaning outside CLR
- Ergo, do not use if building an expression tree for evaluation in, say, SQL Server
- These are lambdas which have an expression on the right side of the
- Statement lambdas
- "The body of a statement lambda can consist of any number of statements; however, in practice there are typically no more than two or three." (Troelsen & Japikse: 7th Ed., pp. 1135‑7)
- Note: I presume that with more extensive lambda calculus you move the statements into their own method & then employ a call to that method from you lambda
- Predictably, you wrap multiple statements inside scope brackets
- The
FindAll()
method ofList<T>
- General
- Use the
FindAll()
method ofList<T>
when you want to extract a subset of that collection - The
FindAll()
method is used point to a different method - The signature of that method must be
bool SecondMethod<T>()
FindAll()
passes each item inList<T>
into the pointed-to method- When the pointed-to method returns
true
theFindAll()
method adds the passed-in object to a "newList<T>
that represents the subset…" (Troelsen & Japikse: 7th Ed., p. 388)
- Use the
List<T>.FindAll()
& thePredicate<T>
.NET (generic) delegate- Not surprisingly, one uses a delegate within
FindAll()
- In fact, .NET has a delegate,
Predicate<>
, which one uses withinList<T>.FindAll()
- Definitions:
public delegate bool Predicate<T>(T obj);
public List<T> FindAll(Predicate<T> match) - Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 388‑9):
static void TraditionalDelegateSyntax()
{
// Make a list of integers…
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });
// Call FindAll using traditional delegate syntax…
Predicate<int> callback = new Predicate<int>(IsEvenNumber);
List<int> evenNumbers = list.FindAll(callback);
// Do something with evenNumbers…
}
static Boolean IsEvenNumber(int i)
{ return (i % 2) == 0; }
- Not surprisingly, one uses a delegate within
- Implementing
List<T>.FindAll()
via anonymous methods- As with most delegates, you can simplify your code by use on an anonymous method
- You eliminate having to create a
Predicate<T>
type explicitly - Syntax/example: (adapted from Troelsen & Japikse: 7th Ed., pp. 388‑9)
static void AnonymousMethodSyntax()
{
// Make a list of integers…
List<int> list = new List<int>();
list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });
// Call FindAll using anonymous method syntax…
List<int> evenNumbers = list.FindAll(
delegate(int i)
{
return (i % 2) == 0;
}
);
// Do something with evenNumbers…
}
List<T>.FindAll()
and lambdas- The
FindAll()
method ofList<T>
is frequently used with lambda expressions - Significantly simplifies your code
- Lambda example:
List<T> evenNumbers = list.FindAll(i => (i % 2) == 0);
// Do something with evenNumbers…
- The
- General
- Lambdas & single-statement member implementations
- As of .NET 4.6 use of lambda expression,
=>
, has been expanded - In particular, if you have a single line of code implementation:
- Can omit curly brackets & use lambda expression instead
- Can do this in methods, properties, & custom operator or conversion routines
- This new use is not resticted to instances involving delegates or events; can be used anywhere
- Syntax/example:
// Lambdas with simple delegate handling…
MyObject obj = new MyObject();
obj.someEvent += (sender, e) => Console.WriteLine(e.msg); - Syntax/example:
class OldMathNewMath
{
public int MultiplyOld(int x, int y)
{
return x * y;
}
// Using lambdas to rewrite a simple method…
public int MultiplyNew(int x, int y) => x * y;
}
- As of .NET 4.6 use of lambda expression,
- General
- General
- Misc. Advanced C# Language Features
- Indexer methods
- General
- When defining custom classes & structures can include an indexer method, which allows you to have your class or structure behave just like an array
- You do this by:
- Having your custom collection implement
IEnumerable
- Including a private
ArrayList
, which is the guts of the collection - Adding a special property definition, which makes use of
this[]
syntax, to return from & add to the internalArrayList
- Note: because we are using the non-generic
ArrayList
the propertyget-er
has to cast the object returned from theArrayList
- Having your custom collection implement
- Definition syntax:
public class WidgetCollection : IEnumerable
{
private ArrayList arWidgets = new ArrayList();
// Now add custom indexer…
public Widget this[int index]
{
get { return (Widget)arWidgets[index]; }
set { arWidgets.Insert(index, value); }
}
} - Usage syntax:
WidgetCollection myWidgets = new WidgetCollection();
// Adding objects…
myWidgets[0] = new Widget( /* set ctor ppts… */ );
myWidgets[1] = new Widget( /* set ctor ppts… */ );
// etc… - Why use custom indexers?
- Technically you can accomplish all this with custom
AddWidget()
&GetWidget()
methods - However, using indexer methods makes your class more compatible with other .NET types
- That said, more often than not your best approach is to use generic collections (esp.
List<T>
)
- Technically you can accomplish all this with custom
- Indexing data using string values
- Use
System.Generic.Dictionary<Tkey, Tvalue>
rather than anArrayList
inside your custom collection class - Definition syntax:
public class WidgetCollection : IEnumerable
{
private Dictionary<string, Widget> dictWdgts =
new Dictionary<string, Widget>();
// Custom indexer based on serial number…
public Widget this[string serialNo]
{
get { return (Widget)dictWdgts[serialNo]; }
set { dictWdgts[serialNo] = value); }
}
// Optionally add method for ClearAll(), property for Count…
} - Usage syntax:
WidgetCollection myWidgets = new WidgetCollection();
// Adding objects…
myWidgets["ABC123"] = new Widget( /* set ctor ppts… */ );
myWidgets["XYZ456"] = new Widget( /* set ctor ppts… */ ); - Again, more often then not you are better off simply using generics than creating a custom collection class
- Use
- Overloading indexer methods
- Can be done
- For example, you might want to be able to access items in a collection by either an index value or a string
- Many .NET types have overloaded indexer methods
- Probably most significant example is ADO.NET's
DataTableCollection
typeDataSet
type sports aTables
property, which returns aDataTableCollection
DataTableCollection
has 3 indices:- Ordinal position (passing in an
int
) - A
string
moniker string
moniker plus namespace (string
)
- Ordinal position (passing in an
- Indexers with multiple dimensions
- You would need to do this if your underlying container is a multi-dimensional array
- This is used by ADO.NET's
DataTable
type, where you pass in a row & column number - When constructing your own you again use the
this
keyword
- Indexer definitions on interface types
- Can define an interface so that any type implementing the interface must implement custom indexing
- Example (for an indexer obtaining Widget objects via an int):
public interface IWidgetContainer
{
Widget this[int index] { get; set; }
}
- General
- Operator overloading
- General
- C# operators which can be overloaded
- Unary:
+, -, !, ~, ++, --, true, false
- Binary:
+, -, *, /, %, &, |, ^, <<, >>
- Comparison operators:
==, !=, <, >, <=, >=
- Note: if you overload one comparison operator you must also overload its logical pair - e.g., if overloading
<
you must also overload>
- Unary:
- C# operators which cannot be overloaded
[]
- though, as just discussed, you can create custom indexers()
- though, as discussed below, you can create custom conversion methods- Shorthand assignment operators:
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
- However, once you overload the related binary operator the related assignment operators overload 'free'
- C# operators which can be overloaded
- Overloading binary operators
- To override any operator C# provides the
operator
keyword operator
keyword can only be used with static methods- "When you overload a binary operator (such as
+
and-
), you will most often pass in two arguments that are the same type as the defining class…" (Troelsen & Japikse: 7th Ed., p. 406)- However, one of your parameters can be of a different type
- Example:
- You have a type
Square
& you overload the*
operator to take aSquare
& anint
- You would likely be writing the code-behind to take the dimensions of the
Square
type & multiply them by theint
to give you a newSquare
- You have a type
- Note: the return type of the operator will also be the same type as the defining class
- An overloaded operator is really no different from any other static method
- To override any operator C# provides the
- The
+=
&-=
operators- These operators are automatically simulated whenever you overload the related binary operator
- Ergo, there is no need - nor ability! - to write overloading code for them
- Overloading unary operators (i.e., increment & decrement operators)
- As unary, you can only pass in a single parameter to operators like
++
&--
- You again employ the
operator
keyword & again must declare your overloaded operator asstatic
- Also, your one parameter must be of the defining class (or structure)
- Syntax/example (
Point
structure):
public struct Point
{
// …
// Add 1 to the X, Y values of the incoming Point…
public static Point operator ++(Point p)
{ return new Point(p.X+1, p.Y+1); }
// Similar code for decrementing…
} - Note re: pre- & post- incrementing & decrementing:
- C++ allows you to overload pre- & post-decrementing separately
- Cannot do this with C#
- However, once you define, say,
++pt
(as above)pt++
is defined automatically for you
- As unary, you can only pass in a single parameter to operators like
- Overloading equality operators
- Should always override for any class implementing
IComparable
System.Object.Equals()
- For reference types the default implemetation of this method is to perform a reference-based comparison
- "can be overridden to perform value-based (rather than referenced-based) comparisons between reference types." (Troelsen & Japiks: 7th Ed., p. 409)
- Notes:
- If you do override in this manner remember that you still have available to you the static
Object.ReferenceEquals(object objA, object objB)
method - As a rule when you override
System.Object.Equals()
you also want to overrideSystem.Object.GetHashCode()
- Once you override
System.Object.Equals()
it is easy to override==
&!=
operators
- If you do override in this manner remember that you still have available to you the static
- Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 409‑10):
public class Point
{
public int X { get; set; }
public int Y { get; set; }
// Ctor…
public Point(int xPos, int yPos) { // … }
// Misc overrides…
public override string ToString() =>
String.Format($"[{this.X}, {this.Y}]");
public override int GetHashCode() =>
this.ToString().GetHashCode();
// Equality operators overrides…
public override bool Equals(object obj) =>
obj.ToString() == this.ToString();
public static bool operator ==(Point p1, Point p2) =>
p1.Equals(p2);
public static bool operator !=(Point p1, Point p2) =>
!p1.Equals(p2);
}
System.IEquatable<T>
interface- Should be implemented on any custom class in which you override
Object.Equals()
- This is especially true if instances of the class are going to be part of any generic collections
System.IEquatable<T>
specifies a single method,Equals(T)
- Interface is used by generic collections such as
Dictionary<TKey,TValue>, <ListT>, & LinkedList<T>
- In particular, in generic collections interface is used by methods like
Contains(), IndexOf(), & Remove()
- See docs.microsoft.com > .NET > C&35; Guide > Programming guide > Statements, expressions, and operators > Equality comparisons > How to: Define Value Equality for a Type
- Excellent discussion of this topic
- Also covers special considerations in overriding equality operator in derived custom classes
- Syntax/example:
public class Point : IEquatable<Point>
{
// Readonly auto-implemented properties…
public int X { get; private set; }
public int Y { get; private set; }
// Note: System.Object.Equals() should NEVER throw an exception…
public override bool Equals(object obj)
{
// Run through the generic implementation…
return this.Equals(obj as Point);
}
// Generic implementation, from IEquatable<T>…
public bool Equals(Point p)
{
// If parameter is null, return false…
if (Object.ReferenceEquals(p, null))
{ return false; }
// Optimization for a common success case…
if (Object.ReferenceEquals(this, p))
{ return true; }
// If run-time types are not exactly the same, return false…
if (this.GetType() != p.GetType())
{ return false; }
// Return true if the fields match…
return (X == p.X) && (Y == p.Y);
}
public static bool operator ==(Point lhs, Point rhs)
{
// Check for null on left side…
if (Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null))
{
// null == null = true…
return true;
}
// Only the left side is null…
return false;
}
// Equals handles case of null on right side…
return lhs.Equals(rhs);
}
public static bool operator !=(Point lhs, Point rhs)
{ return !(lhs == rhs);}
}
- Should be implemented on any custom class in which you override
- Should always override for any class implementing
- Overloading comparison operators
- To overload these your class of course needs to implement the
IComparable
interface - Note: an even better idea is to implement the
IComparable<T>
generic interface - Just as overloading the equality operators was accomplished by internal references to
System.Object.Equals()
implementation, to overload comparison operators you refer toCompareTo()
implementation - Example:
public class Point : IComparable <Point>
{
//…
public int CompareTo(Point other)
{ // Implementation code… }
public static bool operator < (Point p1, Point p2)
{ return (p1.CompareTo(p2) < 0); }
// etc.…
} - Reminders:
- If you oveload
<
operator you must overload>
operator - If you oveload
<=
operator you must overload>=
operator
- If you oveload
- To overload these your class of course needs to implement the
- Internal representation of overloaded operators
- Represented in CIL as hidden methods like
op_Addition, op_Subtraction
, etc. - Each hidden method takes the
specialname
CIL token/decoration - See Troelsen & Japikse: 7th Ed., Table 12-2, p. 452, for full list operator-to-CIL method name mapping
- Represented in CIL as hidden methods like
- Summary
- "Overloading operators is generally useful only when you are building atomic data types" (Troelsen & Japikse: 7th Ed., p. 411)
- In other words, overloading these operators makes sense with types like points & rectangles, not so much with objects like people & database connections
- Many .NET types have overloaded operators
- General
- Custom type conversions
- General/review
- Numerical data types
- You can always store a smaller datatype "in" a variable of a larger datatype without any special coding (implicit casting)
- To go the other way you must make an explicit cast
- Reference types related by inheritance work similarly
- You can always store a derived type as instance of one of its base types
- To go the other way you must make an explicit cast
- Converting between types that are not related by inheritance
- Probably not something which will occur often
- However, you could conceivably want to, say, cast a
Square
as aRectangle
, & visa versa - Could create custom methods to do this
- However, C# also allows you to create methods which employ the
()
casting operator - Notes re: value types (i.e., structures)
- Structures (e.g.,
Point
orSquare
) cannot participate in inheritance - As such, there is no 'natural' way to cast a
Square
as, say, aRectangle
- Even with structures you can, however, implement custom type conversions
- Structures (e.g.,
- Numerical data types
- Creating custom conversion routines
- In addition to
operator
keyword you also make use of eitherexplicit
orimplicit
keyword - When creating custom conversion routines
- Income parameter is always the type you are converting from
- Operator type is the entity you are converting to
- Syntax/example (to convert a
Rectangle
to aSquare
):
public class Square
{
// ctors, methods, etc.…
public static explicit operator Square(Rectangle r)
{ // implementation code… }
} - Inside a
Square
class you could also define a method which casts, say, from aSquare
to aSystem.Int32
- Syntax/Example:
// other code…
public static explicit operator int (Square s)
{ return s.Length; } - In sum, using
explicit
keyword you can write code which will convert either from or to your custom type
- In addition to
- Defining implicit conversion routines
- Two catches
- "…it is illegal to define explicit and implicit conversion functions on the same type if they do not differ by their return type or parameter set." (Troelsen & Japikse: 7th Ed., p. 417)
- "…when a type defines an implicit conversion routine, it is legal for the caller to make use of the explicit cast syntax!" (Troelsen & Japikse: 7th Ed., p. 417)
- Syntax/Example:
public class Rectangle
{
// other code…
// Cast Square to Rectangle by doubling
// the length of the square to get Rect width…
public static implicit operator Rectangle (Square s)
{
Rectangle r = new Rectangle();
r.Height = s.Length;
r.Width = s.Length * 2;
return r;
}
}
- Two catches
- Internal representation of custom conversion routines
op_explicit
keywordop_implicit
keyword
- Final thoughts
- You typically write custom type conversions - if at all - only with structures
- Reference types (i.e., classes), however…
- Can, unlike structures/value types, participate in inheritance
- One notable benefit of inheritance is that it gives you implicit & explicit casting 'for free'
- General/review
- Extension methods
- General
- Introduced with .NET 3.5
- Architecture considerations
- Having added extension methods to the .NET framework, MSFT…
- Genrally discourages their creation
- Feels inheritance is preferred route in vast majority of cases
- Warns/worries that a change in implementation of a type for which a developer cannot change source code might lead to extension methods to break
- States you should never add extension methods as a way to avoid incrementing an assembly's version number for a type which you developed
- For full details see C# Guide > C# Programming Guide > Classes & Struts > Methods > Extension Methods
- All that said…
- There may be cases where extension methods are the best option
- LINQ is really nothing but an expansive set of MSFT-developed extension methods to the
IEnumerable<T>
interface
- Having added extension methods to the .NET framework, MSFT…
- Allows you to add functionality to already-compiled types:
- Classes
- Structures
- Interfaces
- Can also add functionality to types being compiled within your own project
- Restrictions
- Must be defined within a static class (ergo, each extension method must be defined as
static
) - Must use the
this
keyword as modifier on the 1st (& only on the 1st) parameter - Extension methods may be called on either:
- An instance in memory
- Statically, via the defining class
- Must be defined within a static class (ergo, each extension method must be defined as
- Syntax/example:
static class MyExtensions
{
static MyExtensions() { }
// This method allows any integer to reverse its digits…
public static int ReverseDigits(this int i)
{
// Convert int into an array of chars, then reverse them…
char[] digits = i.ToString().ToCharArray();
Array.Reverse(digits);
// Put the chars back into a string…
string newDigits = new string(digits);
// Return the modified string back as an int…
return int.Parse(newDigits);
}
}
- Defining & invoking extension methods
- The parameter to which you apply the
this
keyword/modifier is the type to which you are adding extension methods - You can add other parameters (again, though, only one
this
-modified parameter is allowed) - This, in turn, means you can overload extension methods!
- Significance of
static
&this
keywordsthis
keyword means extension methods can be (& usually are) invoked on an instance of the appropriate type- However, because extension methods are static you can also call them as such
- Syntax (for calling extension methods statically):
MyNamespace.MyExtMethodUtilClass.MyExtMethod(appropriateTypeInstance);
- Name clashes
- Run time engine always looks inside the type to find a method before looking at extension methods
- Therefore, an extension method will never get called if there is an identically named method in the type itself
- You tend to run into name-clash issues when you define interfaces as extension methods
- The parameter to which you apply the
- Extension method scope
- Extension methods cannot access non-public members (i.e., fields) of the type to which the extension methods apply
- Extension does not equal inheritance!
- Extension methods can, however, access property values of an instance of the relevant type
- Importing types that define extension methods
- Common practice - & MSFT recommendation - with extension methods
- Place extension methods in dedicated class libraries
- Create a dedicated namespace for the libraries
- Notes:
- The utility of your extension methods libraries is greatly enhanced, a-hem, by defining the enclosed classes & methods as
public
! - Utilizing dedicated class libraries & namespaces reduces clutter & generally simplifies your coding life
- The utility of your extension methods libraries is greatly enhanced, a-hem, by defining the enclosed classes & methods as
- Extension methods are limited to namespaces that
- Define them
- Import them - i.e., remember to import the appropriate namespace (via
using
keyword)
- Common practice - & MSFT recommendation - with extension methods
- The Intellisense of extension methods
- Extension methods do appear in IntelliSense
- Marked with a blue down-arrow icon
- Extending types implementing a specific interface
- Essentially, this means extending an interface itself
- Approach differs somewhat from that used with classes & structures
- "When you extend an interface with new members, you must also supply an implementation of those members!" (Troelsen & Japikse: 7th Ed., p. 469)
- Supplying an implementation seems to contradict exactly what it is to be an interface
- However, you cannot declare a type of the interface & then invoke the extension method
- The extension method appears only on types implementing the interface
- Remember to take care to use namespaces correctly in all this
- Syntax/example (
using
statements omitted):
// Lamba syntax for single-statement methods used throughout…
namespace MyInterfaces
{
public interface IMyInterface
{ void MyIntfMethod(); }
}
// Define class implementing IMyInterface…
using MyInterfaces;
namespace MyExtMethodsTest
{
public class MyClass : IMyInterface
{
public void MyIntfMethod() =>
Console.WriteLine("MyClass implementation of MyIntfMethod");
}
}
// Define ext methods for classes implementing IMyInterface…
using MyInterfaces;
namespace WhwExtensions
{
public static class MyExtensions
{
public static void ExtMethodStr(this IMyInterface itf, string s) =>
Console.WriteLine($"From ExtMethodStr: {s}");
public static void ExtMethodInt(this IMyInterface itf, int i) =>
Console.WriteLine($"From ExtMethodStr: int = {i.ToString()}");
}
}
// Instantiate class, call all methods…
using WhwExtensions;
namespace MyExtMethodsTest
{
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
mc.MyIntfMethod();
mc.ExtMethodStr("Hello, world!");
mc.ExtMethodInt(4);
Console.ReadLine();
}
}
} - Extending a .NET base class interface
- Do-able
- You could well extend
IEnumerable
- Note: remember that
IEnumerable<T>
itself extendsIenumerable
xx
< xx >
- Syntax/example:
namespace ItfExtensions
{
class Program
{
static void Main(string[] args)
{
string[] bluths= {"George", "Lucille", "Michael", "Job"};
bluths.AnnoyUser();
List<int> mySquares = new List<int>() { 1, 2, 4, 8 };
mmySquares.AnnoyUser();
Console.ReadLine();
}
}
static class AnnoyingExtensions
{
public static void AnnoyUser(
this System.Collections.IEnumerable iterator)
{
foreach(var item in iterator)
{
Console.WriteLine(item);
Console.Beep();
}
}
}
}
- General
- Partial methods
- General
partial
keyword can be used with methods (as well as with classes)- Partial methods appear (at least partly) to be an attempt to allow you to mimic C++ constructs
- In C++ you have header & implementation files
- With C#
partial
methods you:- Prototype your method in one file
- Implement the method in another file
- Requirements/restrictions on partial methods
- Can only be defined within partial classes
- Must return
void
- Can be either static or instance-level methods
- Can have arguments & with one exception (
out
modifier) can have access modifiers - Are implicitly private
- Lastly, note that partial methods are not necessarily emitted into the compiled assembly!
- Writing partial methods
- Set up
- You put the signature of a partial method in one partial class file (you omit scope/implementation braces)
- You put the full definition of the partial method in the other partial class file
- Flag both sets of code with
partial
keyword
- If implementation is not provided
- You will see no trace of the method's definition in the compiled assembly
- You will also see no trace of invocations of the method!
- Partial methods are C#'s version of conditional code compilation
- 'Standard' preprocessor directives are:
#if, #elif, #else
, &#endif
- However, partial methods are strongly typed!
- 'Standard' preprocessor directives are:
- Set up
- Uses of partial methods
- In practice the use of partial methods restricted by the requirements that they:
- Return
void
- Are implicitly private
- Return
- Where they can be useful
- Someone building part of a
partial
class would have to write elaborate implementation code for a method - Partial methods are a superior option versus:
- Using preprocessor directives
- Supplying dummy implementation to virtual methods
- Throwing
NotImplementedException
objects
- Someone building part of a
- Lightweight events
- Most common use of partial methods
- Enables class designers to provide method hooks which co-developers can implement or not
- Naming convention is to use
On
as a naming prefix for such methods
- In practice the use of partial methods restricted by the requirements that they:
- General
- Anonymous types
- General
- Essentially an extension of anonymous methods concept
- For use where you need a class to
- Model encapsulated/related data
- You need no methods, events, etc.
- Work only within current app
- In other words, this is where you need a 'temporary' class
- To create you use:
var
keyword- Object initialization syntax
- Syntax/example:
var homeTown = new { City = "New Providence",
State = "NJ", ZipCode = "07974" }, Pop = 13308; - Property values can then be retrieved via common dot syntax, e.g.,
homeTown.City, homeTown.State
, etc. - The variable you define with
var
gets implicitly typed
- Internal representation of anonymous types
- Anonymous types derive from
System.Object
, & automatically provide implementatioms of the expected virual methods:ToString()
GetHashCode()
Equals()
GetType()
- If you get the
GetType().Name
property on an anonymous type you will get something like:<>f_AnonymousType0`3
- From object initialization syntax compiler generates:
private readonly
fields- Identically named
readonly
properties
- Anonymous types derive from
- The implementation of
ToString()
&GetHashCode()
- The
ToString()
method of an anonymous object will essentially yield your object initialization syntax (including braces) - The
GetHashCode()
method- Uses property names & values as inputs
- Ergo, 2 anonymous types will give you the same hash code if & when they have identically-named properties & values for those properties
- As such, anonymous types play well with
Hashtable
containers
- The
- The semantics of equality for anonymous types
System.Object.Equals()
- Uses value-based semantics when operating on anonymous types
- Therefore, 2 anonymous types will show as equal if defined with the same property/value pairs
==
&!=
operators- These are not overridden by the compiler when creating anonymous types
- Ergo, these operators are reference- (rather than value-) based
- Note re: underlying type of anonymous types
- Two anonymous types with identical property name/value pairs will have identically named underlying types
- Compiler only generates a new underlying type if there is a new property name or value
- Anonymous types containing anonymous types
- Can be done
- Syntax/example:
var purchaseInfo = new {
TimeBought = DateTime.Now,
ItemBought = new { Color = "Red",
SKU="123abc", Descr="Widget" },
Price = 19.99 };
- Summary
- You typically use anonymous types only with LINQ
- Limitations to anonymous types
- You cannot name them
- They always extend
System.Object
- Their fields & properties are read-only
- They cannot support
- Events
- Custom methods
- Custom operators
- Custom overrides
- There is no inheritance (anonymous types are implicitly sealed)
- They lock you in to their default constructor
- Useful in LINQ when you (again) want simply to model the shape of an object
- General
- Pointer types
- General
- Two major types of .NET data:
- Value types
- Reference types
- Pointer types represent a 3rd type
- Using pointer types, for better & for worse, means that you hare handling memory management yourself
- C# pointer-related operators & keywords (many/most recognizable from C++)
*
- Creates a pointer variable - a variable that represents a direct location in memory
- Also used for pointer indirection
&
- gives you the address of a variable in memory->
- Used to access fields of a type represented by a pointer
- Function is same as that of C# dot operator (though unsafe!)
[]
- Allows you to index the slot pointed to by a pointer variable++
,--
- Used to increment/decrement pointer types+
,-
- Standard addition/subtraction on pointer types==
,!=
,<
, etc. - Standard equality & comparison operatorsstackalloc
- Can only be used in an unsafe context
- Allows you to place C# arrays directly on the stack
fixed
- Can only be used in an unsafe context
- Temporarily fixes a variable, allowing you to find variable's address
- Caveats
- If you start manipulating pointers CLR essentially says "You're on your own now, buddy…"
- Will rarely, if ever, have to use pointer types in .NET
- The 2 cases where you might conceivably user pointer types
- "You are looking to optimize select parts of your application by directly manipulating memory outside the management of the CLR." (Troelsen & Japikse: 7th Ed., p. 480)
- You are making a call to a C-based
.dll
or to a COM server, & the method demands a pointer type for one or more of its parameters - Note: in the 2nd case you are typically better off using:
System.IntPtr
type- Members of the
System.Runtime.InteropServices.Marshall
type
- Setting up a project where you will use pointer types
- Must inform compiler that you will employing unsafe code
- If working from the command line include the
/unsafe
flag as an argument (i.e.,csc /unsafe *.cs
) - In VS
- Two major types of .NET data:
- The
unsafe
keyword- Any & all pointer-related code must be written within the scope of the
unsafe
keyword - All other code is considered safe by default
- As necessary,
unsafe
keyword can be used to flag classes, structures, type members, & parameters - Any calls to unsafe methods must themselves be placed within an
unsafe
scope
- Any & all pointer-related code must be written within the scope of the
- Working with the
*
and&
operators- In C & C++ the
*
operator is applied to the variable names - In C# the
*
operator is applied to the type identifier - Syntax/examples:
// C and C++…
int *aa, *bb;
// C#…
int* x, y; - Other syntax examples:
unsafe static void SomeMethod()
{
int myInt;
// Assign an int pointer the address of myInt…
int* ptrToMyInt = &myInt;
// Use pointer indirection to assign value…
*ptrToMyInt = 456;
}
- In C & C++ the
- Using the
ref
keyword as an alternative to using pointers- Troelsen (Troelsen & Japikse: 7th Ed., p. 434) has great examples of an unsafe & a safe Swap function
- Unsafe example:
unsafe public static void UnsafeSwap(int* i, int* j)
{
int temp = *i;
*i = *j;
*j = temp;
} - Safe example:
public static void SafeSwap(ref int i, ref int j)
{
int temp = i;
i = j;
j = i;
}
- Field access via pointers (the
->
operator)- Assume you have defined a
Point
structure, withint
propertiesX, Y
- Field access syntax:
unsafe static void FieldPointing()
{
Point point;
Point* p = &point;
p-> = 123;
Console.WriteLine(p->ToString());
} - Note: Once you use the deallocation operator (
*
) you can revert to dot notation! - Pointer indirection syntax:
unsafe static void UsingPointerIndirection()
{
Point point;
Point* p = &point;
(*p).x = 789;
}
- Assume you have defined a
- The
stackalloc
keyword- When working with pointers you sometimes need a local variable that allocates memory from the call stack
- Note: once you do this that memory is out of reach of .NET garbage collection
- Syntax/example:
unsafe static void UnsafeStackAlloc()
{
char* p = stackalloc char[256];
for (int k = 0; k < 256; k++)
p[k] = (char)k;
}
- Pinning a type via the
fixed
keyword- When using the
stackalloc
keyword memory is cleaned up as soon as thestackalloc
method is done - This, after all, is the nature of memory on the stack
- However, things get more complicated with reference types
- Reference types, of course, exists on the garbage-collected heap
- This means (under normal circumstances)
- The item can be garbage collected at any moment
- The item can also be moved in memory, as the GC process optimizes memory
- You run into obvious problems, then, when you have pointers pointing to reference types
fixed
keyword obviates these issues by pinning a variable in memory during code execution- Note: Any time that you reference a managed variable from
unsafe
code C# compiler will require use of thefixed
keyword - See Troelsen & Japikse: 7th Ed., p. 436, for sample code
- When using the
- The
sizeof
keyword- Gives you the size (in bytes) of a value type
- Cannot be used with reference types
- Syntax/example:
unsafe static void SizeOfUse()
{
int i = 123;
Console.WriteLine("{0}, sizeof(short));
Console.WriteLine("{0}, sizeof(i));
}
- General
- Indexer methods
- LINQ to Objects
- General
- "The term 'LINQ to Objects' refers to the use of LINQ queries with any IEnumerable or IEnumerable<T> collection directly, without the use of an intermediate LINQ provider or API such as LINQ to SQL or LINQ to XML." docs.microsoft.com > .NET > C# Guide > C# Programming Guide > Programming Concepts > LINQ > LINQ to Objects
- Typical enumerable collections on which you will use LINQ to Objects
- A simple
Array
List<T>
Dictionary<TKey, TValue>
- A simple
- Prior to LINQ query logic had to be embedded in
foreach
loops - Especially as the complexity of your query logic grows LINQ to Objects is a big advantage over working with
foreach
loops
- LINQ overview
- General
- LINQ introduced with .NET 3.5
- Prior to LINQ .NET programmers had to use a variety of APIs, based on type of data they wanted to access
- Relational data:
System.Data.dll
System.Data.SqlClient.dll
- Others
- XML document data:
System.Xml.dll
- Assembly metadata:
System.Reflection
namespace - Collections:
System.Array
System.Collections
namespaceSystem.Collections.Generic
namespace
- Relational data:
- Will still make use of these assemblies, types, & namespaces
- LINQ goes a long way, however, to standardize data retrieval & manipulation code
- "Types" of LINQ:
- LINQ to Objects
- LINQ to XML
- LINQ to DataSet
- LINQ to Entities
- Parallel LINQ
- A huge benefit of LINQ expressions is that they are strongly typed
- LINQ-specific programming constructs
- Implicitly typed local variables
- Initializer syntaxes
- Lambda expressions
- "C# LINQ query operators are simply a shorthand notation for calling true blue methods on a class named
System.Linq.Enumerable
." (Troelsen & Japikse: 7th Ed., p. 442) - Will almost always be working with delegates -
Func<>
in particular
- "C# LINQ query operators are simply a shorthand notation for calling true blue methods on a class named
- Extension methods
- You rarely write extension methods to work with LINQ
- However, LINQ expressions are, in fact, shorthand calls on underlying extension methods
- Most of these methods are in
System.Linq.Enumerable
utility class - These methods typically take delegates as parameters - esp. the
Func<T>
delegate
- Anonymous types
- General
- LINQ's role
- General
- A huge part of any software program involves retrieving & manipulating data
- Again, LINQ goes a long way towards standardizing how that is done within .NET
- LINQ 'flavors,' & what they refer to
- LINQ to Objects - applying LINQ queries to arrays & collections
- LINQ to XML - using LINQ to query & manipulate XML documents
- LINQ to DataSet - applying LINQ queries to ADO.NET
DataSet
objects - LINQ to Entities - using LINQ queries within ADO.NET Entity Framework API
- Parallel LINQ (PLINQ) - utilizing parallel processing of data from a LINQ query
- LINQ expressions are strongly typed
- This means C# compiler will ensure that your expressions are syntactically correct
- Will be able to make use of VS IntelliSense
- The core LINQ assemblies
System.Core.dll
System.Data.DataSetExtensions.dll
System.Xml.Linq.dll
- Note: When using LINQ make sure your code imports the
System.Linq
namespace
- General
- Applying LINQ parameters to primitive arrays
- General
- Assume you are working with a string array
- Syntax/example:
string[] someBluths =
{"Lindsey", "Tobias", "Michael", "George Michael"}; - What's interesting about getting
string
results from a LINQ expression is theIEnumerable<string>
expression - Syntax/example:
IEnumerable<string> mBluths =
from b in someBluths
where b.Contains("Michael")
orderby b select b;
- Querying without LINQ
- Certainly doable, though more verbose
- You are forced to use loops &
if
statements - See Troelsen & Japikse: 7th Ed., p. 447, for a non-LINQ, "longhand" version of their LINQ example
- Reflecting over a LINQ result set
- In above example
mBluths
is of typeOrderedEnumerable<TElement, TKey>
- In CIL
OrderedEnumerable<TElement, TKey>
will show asOrderedEnumerable`2
- "Many of the types that represent a LINQ result are hidden by the Visual Studio object browser. These are low-level types not intended for direct use in your applications." (Troelsen & Japikse: 7th Ed., p. 448)
- Can, however, use
ildasm.exe
orreflector.exe
to see these hidden types
- In above example
- LINQ & implicitly-typed local variables
- Unlike the above example, you often want to use LINQ on datasets where you do not know at design time what the underlying type will be
- Even if you do know the underlying type of the LINQ-populated subset the resulting type can be maddening to anticipate
- For instance…
- Assume you run a LINQ query on an
int
array - You will define the subset as
IEnumerable<int>
- The type which implements the
IEnumerable<int>
interface is a low-level class calledWhereArrayIterator<T>
!!
- Assume you run a LINQ query on an
- Rather than capture your LINQ results as an
IEnumerable<T>
generic you really always want to catch the results as an implicitly-typed local variable - Syntax/example:
// Preliminary code…
var mBluths = from b in someBluths
where b.Contains("Michael") orderby b select b; - Syntax/example (to iterate over LINQ results captured as implicitly-typed local variable):
// Preliminary code…
for (var b in mBluths)
{ // Iteration code… } - Notes:
- When you use an implicitly-typed local variable the real return type almost always implements the generic
IEnumerable<T>
interface - Troelsen & Japikse claim (7th Ed., p. 449) that there is no need to worry about what the true underlying type of your
var
is - just iterate over the damned thing
- When you use an implicitly-typed local variable the real return type almost always implements the generic
- LINQ & strongly-typed implicitly-typed local variables
- This can be done, simply by specifying the type in your LINQ query
- Syntax/example:
class Program
{
static void Main(string[] args)
{
// Populate array of Bluths…
Bluth[] theBluths = GetBluths();
// Strongly-type the LINQ query…
var query = from Bluth b in theBluths
where b.Age > 18
select b;
foreach (Bluth b in query)
Console.WriteLine(b.FirstName + ": " + b.Age);
Console.ReadLine();
}
static Bluth[] GetBluths()
{
Bluths[] arrBluths = new Bluths[]
{
new Bluth
{
FirstName = "George",
LastName = "Bluth",
Age = 65
},
// Add more Bluths…
};
return arrBluths;
}
}
class Bluth
{
internal string FirstName { get; set; }
internal string LastName { get; set; }
internal int Age { get; set; }
}
- LINQ & extension methods
- Note that the
Array
type does not implement the genericIEnumerable<T>
interface - (
Array
only implements the non-genericIEnumerable
interface) - That said, the
System.Linq.Enumerable
static class adds this interface - as well as a large number of others - as extension methods - Use IntelliSense on your
var
subset variable to identify the various interfaces & methods added as extensions
- Note that the
- The role of deferred execution
- LINQ results are not actually run/evaluated until you iterate over the results!
- Therefore, if there is some change to the data upon which you are running your LINQ query…
- You do not need to re-execute the LINQ statement
- Simply re-running your
foreach
(or appropriate iterator) statement will give you the accurate results
- Stepping through query results
- Set your breakpoint at the
foreach
line, not at the LINQ code - The cursor will actually jump back & forth to the LINQ query as you step through the
foreach
code
- Set your breakpoint at the
- The role of immediate execution
- You may not always want to use a
foreach
statement to evaluate the results of a LINQ query - There are a number of extension methods available here:
ToArray<T>()
ToDictionary<TSource, TKey>()
ToList<T>()
- Others
- To invoke these methods you typically wrap the entire LINQ statement within parenthesis, & then simply invoke the dot operator
- Syntax/example:
static void ImmediateExecution()
{
int[] lostNumbers = { 4, 8, 15, 16, 23, 42 };
// Populate 2 sets of data NOW…
int[] intArrayLowNos =
(from i in lostNumbers where i < 10 select i).ToArray<int>();
List<int> intListLowNos =
(from i in lostNumbers where i < 10 select i).ToList<int>();
} - Notes:
- Wrapping the LINQ statement in parenthesis serves to cast the results of the statement in 'the' appropriate type (though you really don't care what that type is!)
- As discussed in Generics, if the compiler 'knows' that, say, the underlying type is an
int
- You do not even need to specify type in your code
- Specifically, you could write
( … ).ToArray();
instead of( … ).ToArray<int>();
- Now your result set can be independently manipulated
- Immediate execution is required when you need to pass the results of your LINQ statement to an external caller
- You may not always want to use a
- General
- Returning the result of a LINQ query
- General
- Results of LINQ queries rarely used to instantiate a field
- You cannot define a field as being of type
var
- The target of a LINQ query cannot be instance-level data (i.e., it must be static data)
- You cannot define a field as being of type
- Typically LINQ queries are scoped within a method or property
- The
var
keyword creates some issues, however, because an implicitly-typed local variable cannot be used to define- Parameters
- Return values
- Fields (as just mentioned)
- One way to return the result of a LINQ query to an external caller is to avoid using the
var
keyword & give your method a return type of, say,IEnumerable<string>
- Results of LINQ queries rarely used to instantiate a field
- Returning LINQ results via immediate execution
- The other approach is to use immediate execution
- Give your method a return type of, say,
string[]
- Inside your method capture the results of your LINQ query in a
var
type - Then invoke one of the extension methods on the
var
type to return, say, an array - Syntax/example:
static string[] GetMichaelBluthsAsArray()
{
string[] someBluths = {"Lindsey", "Tobias",
"Michael", "George Michael"};
// Capture results of LINQ query in a var…
var mBluths = from b in someBluths
where b.Contains("Michael") select b;
// Invoke immediate execution, cast as a string array…
return mBluths.ToArray();
}
- General
- Applying LINQ queries to collection objects
- Accessing contained sub-objects
- Recall that LINQ to Objects can be used on any type implementing
IEnumerable<T>
- Therefore, applying LINQ to collections is no different from applying to arrays (done above)
- Notes:
- You do not have to capture the results of you LINQ query in, say, an
IEnumerable<Widget>
variable - You can capture the results in a
var
variable - You can also define your iterator a la
foreach(var w in myWidgets)
- Inside the iteration loop:
- The compiler will recognize your variable
w
as being a widget - This means that you can call properties on that variable - i.e.,
w.Price
- The compiler will recognize your variable
- You do not have to capture the results of you LINQ query in, say, an
- Recall that LINQ to Objects can be used on any type implementing
- Applying LINQ queries to nongeneric collections
- Although the LINQ extension methods have applied
System.Array
with anIEnumerable<T>
implementation, the other members ofSystem.Collections
have not been similarly outfitted - You can still apply LINQ statements to these other non-generic collections
- You apply the
Enumerable.OfType<T>()
extension method Enumerable.OfType<T>()
is one of the few extension methods that does not extend generic types- So long as you are working with a collection object which implements
IEnumerable
" … simply specify the type of item within the container to extract a compatibleIEnumerable<T>
object." (Troelsen & Japikse: 7th Ed., p. 456) - Even better, you use an implicitly-typed local variable to capture that casting
- You apply the
- Syntax/example:
static void UsingLINQOnArrayList()
{
ArrayList someBluths = new ArrayList() { // populate w\ Bluths… }
// Transform ArrayList into an IEnumerable<T>-compatible type…
var enumBluths = someBluths.OfType<Bluth>();
// Now run LINQ…
var mBluths = from Bluth in enumBluths…
}
- Although the LINQ extension methods have applied
- Filtering data using
OfType<T>()
- Remember that non-generics receive
System.Object
types - i.e., anything - A big advantage of using
OfType<T>()
is that when moving data from your non-generic to your generic the method automatically filters your data for you - Syntax/example (adapted from Troelsen & Japikse: 7th Ed., p. 757):
static void OfTypeAsFilter()
{
ArrayList randomStuff = new ArrayList();
// Note AddRange() syntax…
randomStuff.AddRange(new object[]
{ 10, 400, 8, false, new Widget(), "Hello, World!" });
var myInts = randomStuff.OfType<int>();
// Print ints…
foreach(var i in myInts) { // do something… }
}
- Remember that non-generics receive
- Accessing contained sub-objects
- The C# LINQ query operators
- General
- Various LINQ operators
from, in
where
select
join, on, equals, into
orderby, ascending, descending
group, by
- There are also any number of LINQ extension methods, such as
- To transform result sets
Reverse<>()
ToArray<>()
ToList<>()
- To extract singletons or to perform other set calculations
Distinct<>()
Union<>()
Intersect<>()
- To aggregate results
Count<>()
Sum<>()
Min<>()
Max<>()
- To transform result sets
- Various LINQ operators
- Basic selection syntax
- Very similar to SQL syntax
- Difference: In LINQ the
select
statement comes at the end of the entire query statement
- Obtaining subsets of data
- As with SQL you use a
where
clause to filter your query - For compound
where
clauses use any valid C# operator (e.g.,&&, ¦¦, <
, etc.) - Syntax/example:
// Preliminary code…
var adultMichaelBluths = from b in Bluths
where b.name.Contains("Michael") && b.age > 18 select b; - Note: once you obtain your subset from running your LINQ query you have 2 syntax options for looping through that subset
- 1) Declare your
foreach
variable as its underlying typevar
- e.g.,foreach(Bluth amb in adultMichalBluths)…
- Even when declaring as an implicitly-typed local variable the compiler - and intellisense - recognizes it as a Bluth
- Thus, inside the scope of the
foreach
loop you can still write code such asConsole.WriteLine{$"{b.Name} is {b.Age} years old."};
- 2) Declare your
foreach
variable avar
- e.g.,foreach(var amb in adultMichalBluths)…
- 1) Declare your
- As with SQL you use a
- Projecting new data types
- You can design a
select
statement that creates a new, anonymous type - Suppose you have an array of
Employee
objects, whereEmployee
contains not just name but birthdate, id number, start date, & more - From that you simply want a list of employees (i.e., names only)
- Syntax/example:
static void GetAlphaListOfEmployees(Employee[] arrEmpls)
{
var alphEmpls = from e in arrEmpls
select new { e.LName, e.FName };
} - Limitations
- The type you will be creating will not exist until compile time, so you must 'capture' that type as a
var
- Again, however, you cannot create a method which returns a
var
- The type you will be creating will not exist until compile time, so you must 'capture' that type as a
- Obtaining results from a method which projects data types
- You can access properties of your anonymous types if you are working within the method which creates those types
- For instance, you could inject the following into the above example:
static void GetAlphaListOfEmployees(Employee[] arrEmpls)
{
// ... above code
foreach (var empl in alphEmpls)
Console.WriteLine($"{e.LName}, {e.FName}");
} - However, to pass the results to a calling method…
- Once you have created & populated your
var
variable you can invoke theToArray()
extension method on that variable - The resulting array is, of course, something you can return from a method
- However, because (again) you do not know the underlying type being created until compile time you cannot invoke
ToArray<T>()
(or create other generic containers) - Further, to 'catch' the results in a calling method
- You must make explicit use of a
System.Array
object - Example:
Array objs = GetAlphaListOfEmployees(arrEmpls);
- You must make explicit use of a
- Once you have created & populated your
- Final word on projecting new data types
- In returning an
array
(or any other non-generic container type which implementsIEnumerable
) you are left with a somewhat unsatisfying collection ofobject
types - An
object
, of course, can be anything, so you lose strongly-typed data - You can, though, invoke the returned objects'
ToString()
method - All in all, your options on passing anonymous types outside of the method which contains the LINQ statement(s) are limited to those just delineated
- In returning an
- You can design a
- Obtaining counts using
Enumerable
- Use the
Count()
method of theEnumerable
class - Syntax/example:
static int NumberMichaelBluths(string[] someBluths)
{
return (from b in someBluths
where b.Contains("Michael") select b).Count();
}
- Use the
- Reversing result sets
- Use the
Reverse<T>()
extension method of theEnumerable
class - Syntax/example:
static void DescendingListEmployees(Employee[] ourEmpls)
{
var allEmpls = from e in ourEmpls select e;
foreach (var empl in allEmpls.Reverse())
{ // Do something… }
}
- Use the
- Sorting expressions
- Use the
orderby
keyword - You typically use it on a property - e.g.,
from e in ourEmpls orderby e.LName select e;
- Can specify
ascending
(default) ordescending
- Use the
- LINQ as a better Venn diagramming tool
Enumerable
class has a number of set-based methods:Except()
Intersect()
Union()
- follows set theory - i.e., no duplicate membersConcat()
- this will not eliminate duplicates
- You, of course, are working with 2 LINQ queries in these cases
- Syntax/example:
var diff = (some LINQ query).Except(second LINQ query);
- Removing duplicates (e.g., from a
Concat()
expression)- Use
Distinct()
method - Syntax/example:
static void RemoveDupesFromConcat()
{
// Assume two overlapping List<string> of Bluths have been created…
var concatBluths = (from b in firstBluths select b)
.Concat(from b2 in secondBluths select b2);
foreach (string s in concatBluths.Distinct())
{ // Some code… }
}
- Use
- LINQ aggregation operations
- In addition to
Count()
method you also have available:Max()
Min()
Average()
Sum()
- As with Count(), invoke these on parenthetically-wrapped LINQ queries
- In addition to
- General
- Internal representation of LINQ query statements
- General
- Behind the scenes, "the C# compiler actually translates all C# LINQ operators into calls on methods of the
Enumerable
class." (Troelsen & Japikse: 7th Ed., p. 466) - Working with
Enumerable
class methods- Most take delegate as arguments
- In particular, many take the
Func<>
delegate as an argument
- Example:
Where()
method ofEnumerable
- This is the method called whenever you use the LINQ
where
query operator Where()
method is overloaded, & second parameter of each method signature is of typeSystem.Func<>
- Definition 1:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
System.Func<TSource, int, bool> predicate) - Definition 2:
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
System.Func<TSource, bool> predicate)
- This is the method called whenever you use the LINQ
Func<>
delegate review- Defined within
System.Core.dll
assembly - This delegate "represents a pattern for a given function with a set of up to 16 arguments and a return value." (Troelsen & Japikse: 7th Ed., p. 466)
- Format for 2 arguments:
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)
- Format for no arguments:
public delegate TResult Func<TResult>()
- Format for 2 arguments:
- Options when invoking methods taking delegates as arguments
- Create a new delegate type, along with target method(s)
- Use an anonymous method
- Use/define a lambda expression
- Note: options all lead to identical results
- Defined within
- While LINQ query operators are by far the simplest way to author LINQ, it is possible to use the approaches delineated below
- Behind the scenes, "the C# compiler actually translates all C# LINQ operators into calls on methods of the
- Building query expressions using the
Enumerable
type & lambda expressions- Syntax/example:
class Program
{
static void Main(string[] args)
{
string[] favBands = { "Beatles", "The Who",
"Foo Fighters", "Weazer", "Pearl Jam" };
QueryBands(favBands);
}
static void QueryBands(string[] rockGroups)
{
// Build a query expressing using extension methods
// granted to the Array via the Enumerable type…
var subset = rockGroups.Where(band => band.Contains(" "))
.OrderBy(band => band).Select(band => band);
// Print out results…
foreach (var band in subset)
Console.WriteLine("Multi-word bands: {0}", band);
Console.ReadLine(); }
} - Notes:
- 'Starting point' is invocation of
Where()
extension method - In turn,
Enumerable.Where()
method requires aSystem.Func<T1, TResult>
delegate parameter - Delegate parameters:
- First:
IEnumerable<T>
compatible data - i.e., the array of strings, which are the data to be processed - Second: the results from the method, "which is obtained from a single statement fed into the lambda expression" (Troelsen & Japikse: 7th Ed., p. 468)
- First:
Where()
method's return value- Not apparent from the code
- In fact, though, it is an
OrderedEnumerable
type
OrderedEnumerable
type in turns calls genericOrderBy()
methodOrderBy()
, not surprisingly, takes aFunc<>
delegate parameter- You fill out that delegate requirement with a lambda expression
- Last portion is the call to the
Select()
method - Above example - rewritten to show all steps:
var bandsWithSpaces = rockGroups.Where(band => band.Contains(" "));
var bandsInOrder = bandsWithSpaces.OrderBy(band => band);
var subset = bandsInOrder.Select(band => band);
foreach (var band in subset)
Console.WriteLine("Multi-name bands I like: {0}", band);
- 'Starting point' is invocation of
- Syntax/example:
- Building query expressions using the
Enumerable
type & anonymous methods- C# lambda expressions are shorthand notations for anonymous methods
- One can then reconstruct LINQ queries using anonymous methods
- Syntax/example:
static void QueryBands(string[] rockGroups)
{
// Build the necessary Func<> delegates using anon methods…
Func<string, bool> searchFilter =
delegate(string band) { return band.Contains(" "); };
Func<string, string> itemsToProcess =
delegate(string s) { return s; };
// Pass the delegates into the methods of Enumerable…
var subset = rockGroups.Where(searchFilter)
.OrderBy(itemsToProcess).Select(itemsToProcess);
// Print out results…
foreach (var band in subset)
Console.WriteLine("Item: {0}", band);
Console.ReadLine();
}
- Building query expressions using the
Enumerable
type & raw delegates- Can always 'go the long way round the barn'
- Syntax/example:
static void QueryBands(string[] rockGroups)
{
// Build the necessary Func<> delegates…
Func<string, bool> searchFilter =
new Func<string, bool>(Filter);
Func<string, string> itemToProcess =
new Func<string, string>(ProcessItem);
// Pass the delegates into the methods of Eunumerable…
var subset = rockGroups.Where(searchFilter)
.OrderBy(itemToProcess).Select(itemToProcess);
// Print out results (as above)…
}
// Delegate targets…
static bool Filter(string band)
{ return band.Contains(" "); }
public static string ProcessItem(string band)
{ return band; }
- Final notes/summary
- Under the hood LINQ query operators do nothing more than invoke extension methods defined by
System.Linq.Enumerable
type - Many of these methods take delegates - typically
Func<>
- as parameters - Can always pass in a lambda expression in lieu of a delegate parameter
- Under the hood LINQ query operators do nothing more than invoke extension methods defined by
- General
- General
- Object Lifetime
- Classes, objects, & references
- Using the
new
keyword is what creates an object in memory (i.e., on the heap) - The associated variable name is simply a reference to that object.
- These variables (not the objects themselves) live on the stack
- Using the
- The basics of object lifetime
- General
- By & large you can - and should - let the CLR take care of managing the heap
- CLR has a garbage collector that in normal circumstances will automatically clean unneeded objects from memory
- An object become a candidate for garbage collection once it is unreachable by any part of your code base - i.e., once all references to that object have gone out of scope
- CLR memory management & the CIL of
new
- The CLR maintains a pointer, called the next object pointer or new object pointer
- Identifies where the next object will be placed on the heap
- Pointer is critical in CLR memory management - esp. when compacting/optimizing memory (done on an as-needed basis)
- When C# compiler encounters the
new
keyword it tells the CIL to generate anewobj
instruction - This
newobj
CIL keyword tells CLR to:- Calculate the amount of memory that will be needed to handle the object
- See if sufficient memory exists on the heap to handle the new object.
- If sufficient space does exist:
- The object's constructor is invoked
- The object is placed in memory at the next object pointer
- The calling method is given a reference to the object in memory
- The next object pointer advances to the next available slot on the heap
- If sufficient space does not exist the CLR performs a garbage collection (details below)
- The CLR maintains a pointer, called the next object pointer or new object pointer
- Setting object references to
null
- Compiler generates CIL code to ensure that the reference no longer points to an object in memory
- Specifically, compiler cause CIL to:
- Generate a
ldnull
opcode (this pushes anull
value onto the virtual execution stack) - Generate a
stloc.0
opcode (this sets thenull
reference on the variable)
- Generate a
- Note: none of this, however, forces the CLR to activate the garbage collector!
- In sum, when setting a variable equal to
null
all you are doing is "explicitly clipping the connection between the reference and the object it previously pointed to." (Troelsen & Japikse: 7th Ed., p. 477)
- General
- Application roots & garbage collection
- "Simply put, a root is a storage location containing a reference to an object on the managed heap." (Troelsen & Japikse: 7th Ed., p. 477)
- Types of roots:
- References to global objects (though C# does not allow these)
- References to static objects/fields
- References to local objects
- References to object parameters
- References to objects that need to be finalized
- "Any CPU register that references an object" (Troelsen & Japikse: 7th Ed., p. 477)
- Object graphs
- These are used to document all reachable (i.e., rooted) objects
- See Troelsen & Japikse: 7th Ed., Figure 13-3, p. 478, for a good illustration of object graphs
- Garbage collector will never graph the same object twice (this was apparently a bug in COM programming)
- At garbage collection time CLR will review the object graph & mark as garbage any unreachable object
- Components of garbage collection
- Unreachable objects are removed from memory
- Remaining memory is compacted
- Active application roots, & underlying pointers, are updated
- Next object pointer is reset
- Note: there are in fact two heaps
- In addition to the regular one this is another for very large objects
- In practice one can ignore this distinction
- Object generations
- An algorithm is used to obviate the CLR having to examine every object during GC
- Each object is assigned to a generation
- Underlying logic: the longer an object has been in memory the more likely it is to be something that will be required throughout the life of the application
- Generations (.NET specifies 3):
- Generation 0: those objects never marked for collection (i.e., new objects )
- Generation 1: objects that have survived one collection (because there was sufficient space on the heap at the time)
- Generation 2: those objects which have survived more than 1 collection
- Note: Generations 0 & 1 are called ephemeral collections
- How a garbage collection handles generations
- GC 1st examines Gen 0 objects , & removes any which are no longer reachable
- If that sweep generates enough free memory the remaining objects are promoted to Gen 1 & further GC stops
- If more memory is still needed the GC moves on to Gen 1, &, if necessary, Gen 2 objects
- Advantage of this GC approach
- New objects are examined quickly when memory is needed
- Conversely, "older objects … are not 'bothered' as often." (Troelsen & Japikse: 7th Ed., p. 480)
- Garbage collection
- Concurrent garbage collection under pre .NET 4.0
- Pre-.NET 4.0 the CLR used concurrent garbage collection - all active threads would be suspended whenever the 2 ephemeral generations were being worked through
- This was done to make sure no object was accessing the heap during GC
- If objects in (the non-ephemeral) Gen 2 were being garbage collected that process took place on a dedicated thread
- Purpose of this model was to minimize interruptions to an app during GCs
- Background garbage collection under .NET 4.0 & later
- Note: In reading Troelsen & Japikse: 7th Ed. (pp. 480‑1) it's frankly difficult for me to understand the difference between pre-4.0 GC & 4.0'S background garbage collection.
- Whatever the difference, .NET 4.0 GC is apparently an improvement in efficiency & predictability
- The
System.GC
type- General
- "
System.GC
… allows you to programmatically interact with the garbage collector using a set of static members." (Troelsen & Japikse: 7th Ed., p. 481) - Rare, if ever, that you will use this - it is essentially designed to handle internal use of unmanaged resources
- Partial list of members (see Troelsen & Japikse: 7th Ed., Table 13-1, p. 481, for details):
AddMemoryPressure() & RemoveMemoryPressure()
- must use in tandemCollect()
- force a garbage collectionCollectionCount()
- how many times a generation has been sweptGetGeneration()
- for a single objectGetTotalMemory()
MaxGeneration
(currently = 3)SuppressFinalize()
- see belowWaitForPendingFinalizers()
- typically invoked immediately after callingGC.Collect()
- As of .NET 3.5 SP1:
- Can be notified when a GC is about to occur
- Troelsen believes this feature is of limited use
- "
- Forcing a garbage collection
- Two circumstances where you might want to force a GC:
- You are about to enter a section of code which you do not want interrupted by a GC
- Your app has created a large number of objects and you want to free memory ASAP
- Syntax:
GC.Collect();
GC.WaitForPendingFinalizers(); WaitForPendingFinalizers()
method- Allows finalizable objects the time to finish their clean-up before your app continues
- Suspends calling thread to prevent your code calling methods on objects currently being destroyed!
Collect()
method- Can pass in a parameter specifying the oldest generation to be GC'ed
- Can also pass in a
GCCollectionMode
enumeration (3 values) as a parameter to fine-tune how the GC should operate - As with "regular" GC,
Collect()
promotes surviving generations
- Two circumstances where you might want to force a GC:
- General
- Concurrent garbage collection under pre .NET 4.0
- Building objects with GC in mind
- Finalizable objects
- The
Finalize()
method- Member of
System.Object
, so it's available to all objects - Default implementation:
public class Object
{
protected virtual void Finalize() { }
} - Notes:
- The method simply provides a location for clean-up code; method is more abstract than virtual
- Method is protected, so it is not available via an instance's dot operator
- Only the GC can call an object's
Finalize()
method
- When
Finalize()
will be called:- During "natural" garbage collection
- You invoke
GC.Collect()
- The application domain hosting your app is unloaded from memory
- The
Finalize()
method & structures- You cannot implement/override
Finalize()
for structures, because, of course, structures do not even live on the heap - If you have a structure which contains an unmanaged resource you use the
IDisposable
interface
- You cannot implement/override
- You never need to override
Finalize()
if making use solely of managed resources - Two ways .NET interacts with unmanaged code:
- By "directly calling into the API of the operating system using Platform Invocation Services (PInvoke)" (Troelsen & Japikse: 7th Ed., p. 485)
- Complex COM interoperability scenarios (which is typically done via
System.Runtime.InteropServices.Marshall
types) - Ergo, you only need to override
Finalize()
if one of those 2 scenarios exists
- Member of
- "Overriding"
System.Object.Finalize
- You actually do not override finalize
- Instead use (C++-like) destructor syntax
- Syntax:
class MyResourceWrapper
{
˜MyResourceWrapper()
{ //Unmanaged resource clean-up code… }
} - Notes on destructors:
- Never take an access modifier (implicitly
protected
) - No parameters
- Cannot override (i.e., only one destructor is allowed per class)
- Never take an access modifier (implicitly
- Reason for destructor approach is that compiler needs to add to your code
- Never have your destructors refer to managed objects, as you never know whether they might have been destroyed in the GC process
- When you compile a destructor CIL:
- Names/renames the destructor
Finalize()
- Wraps relevant code inside a
try/finally
block
- Names/renames the destructor
- The finalization process
- If an object supports a custom
Finalize
the runtime adds a pointer to the object on an internal "list" called the finalization queue - When GC decides it's time to remove an object from memory it:
- Examines each object on the finalization queue
- Copies each finalizable object off the heap & onto something called the finalization reachable table (a.k.a., the freachable, pronounced "eff-reachable")
- Then the runtime opens a separate thread to invoke
Finalize()
on each object on the freachable - However,
Finalize()
will not be invoked on these objects until the next garbage collection
- All in all, this means finalizable objects will not be truly finalized for at least 2 GCs
- Given all the processing involved, to the extent possibly you really want to avoid including a
Finalize()
method in your classes
- If an object supports a custom
- The
- Disposable objects
- General
- When working with unmanaged resources you may not want to wait for a garbage collection to occur for the resource to be released
- In these cases instead of "overriding"
Finalize()
you implement theIDisposable
interface - Interface:
public interface IDisposable
{
void Dispose()
} Dispose()
& the .NET GC- GC has nothing to do with
Dispose()
- Ergo, structures can implement
IDisposable
- GC has nothing to do with
- Notes:
Dispose()
is invoked by the object userDispose()
method should itself invokeDispose()
on any contained disposable objects- You can rely on an object's
Dispose()
method to invokeDispose()
on contained objects & structures because the container object itself is not being removed from memory by the GC Dispose()
allows an object to clean up resources without all that is involved in the finalization process (i.e., object being placed on freachable, etc.)
- The one "complication" with
Dispose()
- Make sure an object implements
IDisposable
before invoking the method - Syntax/example (to insure that the object is
IDisposable
):
class Program
{
static void Main(string[] args)
{
// Create a disposable object & then invoke Dispose()…
SomeDisposableObject do = new SomeDisposableObject();
// Use your object. Then, when finished…
if(do is IDisposable)
do.Dispose();
}
} - On the flip side, if an object implements
IDisposable
you should always assume that there is some clean-up code which ought to be run - In other words, always invoke
Dispose()
on objects implementingIDisposable
- A .NET "quirk"
- For some .NET base class libraries it was felt that
Dispose()
was not the most appropriate method name - Ergo, MSFT created duplicate methods for certain .NET base class libraries
- Example:
System.IO.FileStream
has aClose()
method - Where these "alias" methods have been created the underlying code is exactly the same as the code behind
Dispose()
Dispose()
, however, is always available forIDisposable
objects & can always be invoked
- For some .NET base class libraries it was felt that
- Make sure an object implements
- Reusing the C#
using
keyword- "Normally" when using an
IDisposable
object you want to:- Invoke the methods of the object inside a
try
block - Immediately thereafter invoke
Dispose()
inside afinally
block
- Invoke the methods of the object inside a
- In lieu of having to repeatedly create the
try/finally
blocks C# allows a second use for theusing
keyword- Syntax:
using(MyResourceWrapper rw = new MyResourceWrapper())
{ /* Invoke methods on the rw object… */ } - This syntax will insert the requisite
finally { rw.Dispose() }
inside the emitted CIL
- Syntax:
- Can also use this syntax to handle multiple
IDisposable
objects if they are of the same type - Syntax:
using(MyResourceWrapper rw1 = new MyResourceWrapper()
rw2 = new MyResourceWrapper())
{ /* Invoke methods on the rw1 & rw2 objects… */ }
- "Normally" when using an
- General
- Finalizable & disposable objects
- General
- It is possible to create objects that are both finalizable & disposable
- Advantage is that if user forgets to invoke
Dispose()
the GC will ultimately release the unmanaged resources viaFinalize()
- To do this must include
GC.SuppressFinalize(this)
within yourDispose()
code - The
SuppressFinalize()
method tells the GC that the object's destructor does not have to be invoked when the container object is garbage collected
- A formalized disposal pattern
- "Current" drawbacks/concerns:
- Both
Finalize()
&Dispose()
are designed to do the same thing, yet this introduces possibility of duplicate code - You don't want
Finalize()
to remove any managed objects, yet you might wantDispose()
to do precisely that - Want user to be able to invoke
Dispose()
repeatedly without generating errors
- Both
- MSFT recommendation:
- Include a private boolean
disposed
in class - Define 2 versions of
Dispose()
- The 1st version is public & takes no parameters, as required by the
IDisposable
interface - The 2nd version should be private & have this signature:
private void Dispose(bool disposing)
- The 1st version is public & takes no parameters, as required by the
- All code to clean up unmanaged resources should live in the private
Dispose()
method- This private method should be called by both
Finalize()
& the publicDispose()
method - As you might expect, when calling the private version of
Dispose(bool disposing)
:Finalize()
should pass in an argument offalse
Dispose()
should pass in an argument oftrue
- This private method should be called by both
- This pattern serves to ensure that
- Once unmanaged resources are released there is no attempt to do so again should
Dispose()
be invoked multiple times by the object's user - Invocations of
Finalize()
are ignored ifDispose()
has already been invoked
- Once unmanaged resources are released there is no attempt to do so again should
- Syntax/example:
class MyResourceWrapper : IDisposable
{
// To track whether Dispose() has already been called…
private bool disposed = false;
˜MyResourceWrapper()
{
Dispose(false); // …re-direct Finalize()
}
public void Dispose()
{
Dispose(true); // …re-direct Dispose()
GC.SuppressFinalize(this);
}
// ALL clean-up code goes here…
private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{ /* Dispose MANAGED resources… */ }
// Next, clean up UNMANAGED resources…
// Lastly, set flag…
disposed = true;
}
}
}
- Include a private boolean
- Note on disposable objects:
- After running
Dispose()
the object itself may still well be in memory - Therefore, be sure to include traps within appropriate members so that they neither do nor return anything after
Dispose()
has been invoked
- After running
- "Current" drawbacks/concerns:
- General
- Finalizable objects
- Lazy object instantiation
- General
- Need/scenario
- In a custom class you have a property which returns an inner type
- That inner type…
- Requires a large amount of memory
- Is 'expensive' to create, because, for instance
- Invokes a remote method
- Requires communication with a database
- Other
- "As is" the constructor of your container type should instantiate the inner type whenever the container is new-ed
- However, if the container property corresponding to the inner type is never called the instantiation of that inner type needlessly eats up memory
- One can of course write code to handle this scenario more efficiently
- However, .NET 4.0 introduces a
Lazy<>
(obviously generic) class to handle this situation- Defines data that will not be defined unless app requests it
- Must specify the underlying type that will be created on 1st use
- Syntax:
class ContainerObj
{
//We utilize the default ctor of MyInnerType…
private Lazy<MyInnerType> myType =
new Lazy<MyInnerType>();
//Method which returns the inner type…
public MyInnerType GetTheType()
{ return myType.Value; }
} - Note use of
Value
property within thereturn
statement
- Need/scenario
- Customizing the creation of lazy data
- You can quickly envision scenarios where the above syntax is overly simplistic:
- Might want to employ a ctor other than the default one when creating your inner type
- Might have other, related code to run upon instantiating the inner type
- Solution:
- Can specify a generic delegate as an optional parameter with
Lazy<>
class - That delegate specifies a method to call when you create the wrapped type
- Delegate is of type
System.Func<>
- Delegate can point to a method which returns the same data type as the wrapped type
- Troelsen & Japikse observations (see Troelsen & Japikse: 7th Ed., p. 498):
- Will rarely need to specify any parameters inside the
Func<>
delegate - Recommends using a lambda expression to handle the delegate
- See Troelsen & Japikse: 7th Ed., p. 498, for sample code
- Will rarely need to specify any parameters inside the
- Delegate is of type
- Can specify a generic delegate as an optional parameter with
- You can quickly envision scenarios where the above syntax is overly simplistic:
- General
- Classes, objects, & references
- Collections & Generics
- Programming with .NET Assemblies
- Building & Configuring Class Libraries
- Defining custom namespaces
- General
- Creating namespaces
- Can define a single namespace across multiple files/classes within a single assembly
- Can define a single namespace across multiple assemblies (this is done with certain .NET namespaces)
- Creating custom namespaces…
- Important with larger projects
- Especially important when creating *.dlls you wish to share
- References, & importing custom namespaces
*.C#
files within project tab of entire project determines which resources are available for all- When accessing a type in another namespace within the same project must either
- Fully qualify type reference (i.e.,
rootnamespace.childnamespace1…
) - Include
using
statement(s) at top of file
- Fully qualify type reference (i.e.,
- Creating namespaces
- Name clashes
- In C# will get a compiler error if you refer to "a" type that exists in more than 1 namespace
- Resolving name clashes using fully-qualified names
- You are actually not required to employ the
using
keyword - Can fully qualify type reference:
rootnamespace.childnamespace1… &.typename
- However, generally little sense in eschewing
using
keywordusing
keyword saves keystrokes- In CIL code types are always defined with fully-qualified names
- Only time where it makes sense to use fully-qualified names is when you need to resolve name clashes (i.e., you have identically-named types in more than one namespace)
- You are actually not required to employ the
- Resolving name clashes using aliases
- Syntax:
using someAliasName = Rootnamespace.ChildNamespace.TypeYouWant
- An alias name is simply a token, or placeholder, that will be replaced by fully-qualified namespace at runtime
- May also want to use aliases in lieu of lengthy/deep namespace declarations
- Syntax:
- Creating nested namespaces
- Can create additional namespaces via the
namespace
keyword - These namespaces are children of the root namespace
- Can nest namespace declarations in code
- Sometimes the role of a root namespace is simply to provide scope
- i.e., there are no types defined within the root namespace
- In this case can omit
namespace
declaration at root level - Shortcut syntax:
namespace MyRootNamespace.SomeChildNamespace
- Can create additional namespaces via the
- VS & default namespaces
- VS creates a default namespace (also called the root namespace) for each assembly
- Root namespace set in project window in VS
- Default name for root namespace is the name of project itself
- Name of root namespace can be changed
- General
- .NET assemblies
- Role
- General
- .NET assembly: "a versioned, self-describing binary file hosted by the CLR" (Troelsen & Japikse: 7th Ed., p. 510)
*.exe
or*.dll
file extensions
- Code reuse
- Executable assemblies can certainly make use of defined types in other executable assemblies
- In other words, an executable can be considered/set up as a code library
- Generally, though, one creates
.dll(s)
for this - Language neutrality
- Type boundaries
- Another layer of identification above namespaces
- Full qualification of a class =
assembly.parentnamespace.childnamespace.granchildnamespace…class
- Versionable units
- Version number format: major.minor.build.revision
- When used in conjunction with (optional) public key value allows multiple versions to exist on same machine
- Note: When a public key value is used the assembly is termed strongly named
- Self-describing (via manifest)
- So-called because .NET assemblies record every external assembly they require
- Manifest also describes composition of every contained type
- Because of manifest .NET assemblies, unlike COM assemblies, do not need to consult Windows system registry
- Configurable
- Private vs. shared assemblies
- Private assemblies exist in directory or sub-directory of the consuming application
- Shared assemblies reside in global assembly cache, aka GAC
*.config
file (XML)
- Private vs. shared assemblies
- General
- Format (i.e., .NET assembly file components)
- Win32 file header
- Establishes that an assembly can be run under Windows OS
- Defines kind of app (console vs. GUI vs.
*.dll
code library) - To view: run
dumpbin.exe
at VS command prompt and include/headers
flag
- CLR file header
- Block of data all .NET files must support
- Automatically generated by VS compiler
- Defines flags that enable the runtime to understand the layout of the assembly
- Sample flags: location
- Location of the metadata
- Location of resources within the file
- Version of the runtime the assembly was built against
- To view: run
dumpbin.exe
at VS command prompt and include/clrheader
flag - Note: somewhat ironically, data in CLR file header are represented by an unmanaged C-style structure
- CIL code, type metadata, & the assembly manifest
- CIL code is, of course, the core of an assembly
- Again, CIL is platform- & CPU-agnostic
- CIL compiled at runtime (by JIT-er) according to CPU & platform instructions
- Metadata - completely describes format of:
- Internal types
- External types referenced by assembly
- Metadata used to:
- Resolve location of types (& of types' members) within the binary
- Place types into memory
- Facilitate remote method invocations
- Assembly manifest documents/specifies:
- Each module within an assembly
- Assembly version
- References to external types
- CIL code is, of course, the core of an assembly
- Optional assembly resources (embedded)
- These can be images, icons, etc.
- If you have a lot of these kinds of items you can place them in satellite assemblies
- Multi-file assemblies
- Note: most .NET assemblies are single-file
- Module: generic term for a valid .NET binary file
- Multi-file assemblies: multiple .NET
*.dlls
deployed & versioned as a single logical unit - One module will be the primary module
- Primary module contains the assembly-level manifest (plus CIL code, metadata, etc.)
- Secondary modules
- Naming convention (not a requirement):
*.netmodule
file extension - Contain CIL code, type metadata, and module-level manifest
- Naming convention (not a requirement):
- Advantages of multi-file assemblies
- Fast download times
- Development flexibility
- Win32 file header
- Role
- Building & consuming assemblies
- Single-file
- The manifest in
ildasm.exe
- First code block specifies all external assemblies referenced
.publickeytoken
will be present only for those external assemblies with a strong name.custom
tokens then follow- These identify assembly-level attributes
- These tokens come from (hidden)
AssemblyInfo.cs
file created by VS - Identify items such as company name, trademark, etc.
- Set assembly-level attributes through button on tab of Solution screen
- Manifest also includes
.assembly
&.module
tokens, which include names of those 2 items
- Exploring the CIL
- Continue using
ildasm.exe
- Can get appropriate details under following tags:
.method
tags.field
tags.property
tags
- Continue using
- Exploring the type metadata (
ildasm.exe
to view) within - Note: language-independence of .NET platform allows for cross-language inheritance
- The manifest in
- Multi-file
- General
- VS does not support a multi-file assembly template
- Ergo, must use command-line compiler (
csc.exe
) to create a multi-file assembly- Use the
/t
flag to create a*.netmodule
file - Note: you want to compile any/all secondary modules first, as you will need a reference to them when compiling the primary module
- Will need to use the following flags when compiling primary module from
csc.exe
/t:library
/addmodule:
- then name of secondary module (e.g.,/addmodule:file2.netmodule
)/out:
- then name of primary module (e.g.,/out:mymainassembly.dll
)- Last argument on command line is name of main
*.cs
file
- Use the
- Can use
ildasm.exe
to explore a*.netmodule
just as you would do with any other .NET assembly - Files are not merged into a single
*.dll
- Main
*.dll
&*.netmodule
s are 'stitched together' via the assembly maifest
- Consuming a multi-file assembly
- When referencing you only need to supply the name of the primary module
- Notes re:
*.netmodule
files…- Do not have individual version numbers
- Cannot be loaded directly by CLR - can only be loaded via primary module
- General
- Single-file
- Understanding assemblies
- Private
- General
- Private assemblies must be located within the directory (called the application directory) or in a sub-directory of the client app
- Note: When you browse to a non-public (i.e., private) assembly when setting project references VS automatically copies that assembly into the client application's
\bin\Debug
folder - Client app does not have to consult Windows registry for location of private assemblies
- This all means that .NET apps and their private assemblies can be relocated on a machine
- Without re-installing
- Without affecting any other application
- Without creating orphaned registry entries
- Moving and/or copying an application and its private assemblies like this is called Xcopy deployment
- Lastly, to uninstall a .NET assembly:
- Can simply delete application directory (& all sub-directories)
- You will not be left with orphaned registry settings!
- Identity of a private assembly
- Friendly name: (primary) module name minus the file extension
- Full identity = friendly name plus numerical version (both are in assembly manifest)
- Because private assemblies are isolated CLR does not use version number when checking for location
- Probing process
- Probing = mapping external assembly request to location of requested binary file
- Implicit request
- CLR consults manifest to find assembly
- CLR refers to
.assembly extern
token to do this
- Explicit request
- Programmatic request, via
Load()
orLoadFrom()
method - These methods are in the
System.Reflection.Assembly
.NET class - Usually done for late binding & dynamic loading of type members
- Programmatic request, via
- How probing works
- CLR begins with friendly name, then appends
.dll
- CLR looks for that file in application directory
- If unsuccessful CLR looks for a subdirectory with the exact name as the assembly's friendly name… and looks there for the file
- If still unsuccessful CLR repeats process but for a file = friendly name plus
.exe
extension - If then unsuccessful CLR throws a
FileNotFoundException
object at runtime
- CLR begins with friendly name, then appends
- Configuring private assemblies
- You must dive into the configuration file if you place private assemblies in a folder other than the application directory or a 'friendly name' directory
- Can control where CLR looks for private assemblies by creating a configuration file
- Configuration files are XML files
- Configuration file must take the same name as the launching application, & then take
*.config
file extension - Example:
MyApplication.exe.config
- Configuration files must be deployed in client's application directory
- Configuration files allow assemblies to be deployed into sub-directories
- The
App.config
file- Root node:
<configuration>
- Next 2 nested elements:
<runtime>
&<assemblyBinding>
<probing>
element- Define via
privatePath
attribute - Note:
privatePath
attribute does not specify which assemblies are located where privatePath
attribute only specifies where private assemblies may be found- Can specify multiple paths with
privatePath
attribute via semi-colon-delimited values - Can only specify sub-directories with
privatePath
attribute (i.e., can specify neither relative nor absolute directories) - Notes:
- If multiple copies/versions of an assembly exist in app directory & subdirectories CLR will load the 1st one it finds
- To specify external directories must use
<codeBase>
element
- Define via
- Example:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="MyLib;MyLib\OtherLibs" />
closing tags…
- Root node:
- Configuration files & Visual Studio
- To add:
- Default name =
app.config
- do not change! - When the app is compiled VS will copy the data in
app.config
into the\bin\Debug
directory and will properly name/rename that*.config
file - Ergo, this process will work even if project is renamed
- General
- Shared
- General
- Single copy of a shared assembly can be used by several applications on a single machine
- Shared assemblies are deployed to the GAC
- Can only install
*.dll
assemblies in the GAC (i.e., cannot install*.exe
's)
- Strong names
- An assembly must be assigned a strong name before it can be deployed to the GAC
- Uniquely identifies publisher of a .NET assembly
- Roughly akin to globally unique identifier (GUID) scheme used by COM
- Formal definition/requirements of a strong name
- Friendly name of the assembly
- Version number (assigned using the
[AssemblyVersion]
attribute insideAssemblyInfo.cs
file) - Public key value (assigned using the
[AssemblyKeyFile]
attribute insideAssemblyInfo.cs
file) - digital signature: created using a hash of the (entire) assembly's contents & its private key value
- Optional: cultural identity value (assigned using the
AssemblyCulture
attribute)
- Strong names are based (in part) on 2 cryptographically related keys:
- public key
- private key
*.snk
file contains the data for the public & private keys*.snk
file is not the strong name itself*.snk
simply pairs the public and private key data
- Once you let compiler know location of
*.snk
file- At compile time records full public key value in assembly manifest
- Identifies in manifest via
.publickey
token
- Digital signature
- Equals combination of hash code & private key
- Note: a hash code is a numerical value that's unique for a given input
- If you change so much as a comma in your code the compiler will generate a new, unique hash code
- Gets embedded in assembly's CLR header data
- Private key
- Not found anywhere inside assembly manifest
- Used to create digital signature
- Equals combination of hash code & private key
- .NET Best Practice: strongly name all assemblies, even private ones
- An assembly must be assigned a strong name before it can be deployed to the GAC
- Creating
*.snk
(Strong Name Key) file- Note: Initially-created
*.snk
files are empty until assembly is compiled - Method 1:
sn.exe
command line utility- Not used much any more
-k
flag instructs tool to generate a new file using public/private key info- Syntax:
sn -k MyKeyPair.snk
- Using the
*.snk
file- Under
AssemblyInfo.cs
file open the - Add the appropriate
[AssemblyKeyFile]
assembly-level attribute - Syntax/example:
[assembly: AssemblyKeyFile(@"C:\Users\Bill\Documents\MyTestKeyPair.snk")]
- Note: Ignore the warnings you might receive
- Under
- Version numbers
- Format:
<major>.<minor>.<build>.<revision>
- Each part can take a value between 0 and 65535
- By default
AssemblyInfo.cs
will contain the following attribute:
[assembly: AssemblyVersion("1.0.0.0")]
- However, you can use the wildcard token to instruct VS to increment build & revision numbers automatically:
[assembly: AssemblyVersion("1.0.*")]
- Format:
- Method 2: VS 2010
- Navigate to tab of project's page
- Check box
- Under select or
- If creating a new file
- Remember to append
.snk
file extension - Can password-protect the
*.snk
file *.snk
file will now appear in VS
- Remember to append
- As you generate new versions re-use the same
*.snk
file - Reminder…
- Can navigate to button via
- Here you can set assembly-level attibutes, such as copyright information, etc.
- Note: Initially-created
- Installing strongly-named assemblies to the GAC
- Preferred methods in production - use either
- Windows MSI installer package
- A 3rd-party installer package, such as InstallShield
- For testing use
gacutil.exe
command-line utility- Note: requires admin rights
- Can use the
/?
flag to see all options/flags - Most frequently-used flags
/i
- for installing a strongly-named assembly to the GAC/u
- for uninstalling an assembly/l
- for listing assemblies in the GAC
- Navigate to directory containing the
*.dll
you want to install - Syntax/example - installing a library:
gacutil -i MyLibrary.dll
- Syntax/example - to verify installation:
gacutil -l MyLibrary
- Preferred methods in production - use either
- Viewing the .NET 4.0 GAC using Windows Explorer
- Prior to .NET 4.0 the GAC was
C:\Windows\assembly
- Now, however, the GAC is bifurcated
- All pre-.NET 4.0 base class library continue to reside in
C:\Windows\assembly
C:\Windows\Microsoft.NET\assembly\GAC_MSIL
- This is where all .NET 4.0+ shared assemblies are located
- Within this there are numerous sub-directories
- One for each code library - including BCLs
- Sub-directory name = friendly name of assembly
- Under these sub-directories are more sub-directories
- Naming convention:
v4.0_major.minor.build.revision_publicKeyTokenValue
- Note: the
v4.0
signifies that the library was compiled against .NET 4.0
- Naming convention:
- All pre-.NET 4.0 base class library continue to reside in
- Prior to .NET 4.0 the GAC was
- General
- Private
- Shared assemblies
- Consuming
- Browsing to assembly location from within VS
- Troelsen & Japikse (7th Ed., p. 541), state that after adding reference to a shared assembly you can see that
False
is set to - I, however, found this not to be the case, & manually set the flag to
false
- (Ergo, shared libraries are not copied to consuming app's
/bin/debug
folder)
- Troelsen & Japikse (7th Ed., p. 541), state that after adding reference to a shared assembly you can see that
- The manifest of a shared assembly
- Public key token recorded in manifest will exactly match public key token in assembly's parent folder in the GAC
- If the assembly is not found in the GAC VS will look for a local (i.e., private) copy of the assembly
- A
FileNotFoundException
is thrown if that fails
- Browsing to assembly location from within VS
- Configuring
- General
- Shared assemblies can be configured via a client
*.config
file- Same as with private assemblies
<privatePath>
element- Not used for shared assemblies because, of course, public assemblies by definition reside in the GAC
- If there is even one private assembly, however, the element might still exist in the
*.config
file
- Working with multiple versions of a shared assembly
- Scenario 1: You discover a bug in version 1.0.0.0 of a code library you have in your GAC
- Assume you distribute corrected code library as version 1.1.0.0
- Distribute a new
*.config
file pointing clients to version 1.1.0.0 of code library
- Scenario 2: You add new functionality & release version 2.0.0.0 of your code library
- Existing client apps will have no knowledge of version 2.0.0.0
- Both versions can reside next to each other in GAC
- New versions of client app can complile against code library 2.0.0.0 & older versions can continue to comple against 1.0.0.0
- Scenario 1: You discover a bug in version 1.0.0.0 of a code library you have in your GAC
- Shared assemblies can be configured via a client
- New versions of a shared assembly
- Building
- After making code modifcations update via
- Reuse existing
*.snk
file (again, visible in ) - Install new version of assembly in GAC
- Inside GAC
- Will now see a new version-specific subdirectory in GAC for updated assembly
- If you used same
*.snk
file the new version of assembly will have same public key (which is what you want!)
- Client apps
- If you recompile client apps VS will automatically reset references to the new version of the shared assembly
- However, if you do not change client app(s) they will continue to point to original shared assembly
- Building
- Dynamically redirecting to specific versions of a shared assembly
- If, as is often the case, you do not want to recomple a client app to use a specific (generally new) version of a shared assembly you modify the client app's
*.config
file - Note: what you are doing with this approach is overriding the client assembly's manifest
- Main steps
- Crack open the client app's
*.config
file (e.g.,MyClientApp.exe.config
) - As with addition of
<probing>
element, within root<configuration>
element nest a<runtime>
element - Within
<runtime>
element:- Nest a
<assemblyBinding>
element - Within that (opening) element include a
xmlns="urn:schemas-microsoft-com:asm.v1"
namespace declaration
- Nest a
- Within
<assemblyBinding>
scope include<dependentAssembly>
(pairs of) elements for each shared assembly you need to control version-use for
- Crack open the client app's
- Inside each
<dependentAssembly>
scope include 2 empty elements:<assemblyIdentity>
- & include following attributes & values:name="SharedAssemblyFriendlyName"
publicKeyToken="token…"
culture="neutral"
(can pick values other than"neutral"
)
<bindingRedirect>
- & include following attributes:oldVersion
(set value equal to old version number, e.g., 1.0.0.0)newVersion
(set value equal to new version number, e.g., 2.0.0.0)- Note: can specify a range for
oldVersion
values - e.g.,"1.0.0.0-1.5.0.0"
- Syntax/example:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="CarLibrary"
publicKeyToken="8fc57dba00ce65d2"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0-1.2.0.0"
newVersion="2.0.0.0" />
closing tags…
- Note: I could not get this whole approach to work! (following example in Troelsen & Japikse: 7th Ed., pp. 542-77)
- If, as is often the case, you do not want to recomple a client app to use a specific (generally new) version of a shared assembly you modify the client app's
- Publisher policy assemblies
- General
- The just-described dynamic redirection approach only controls a single installation of a client app
- You obviously will often run into situations where you want all instances of client apps to bind to a new version of a shared library
- Publisher policy enables you to install a binary version of a
*.config
file in the GAC- Much easier than configuring & deploying
*.config
files to each client machine - Note: over time you can easily lose track of which machines and/or apps even use a shared library
- Removes the need even to have those
<assemblyBinding>
elements in a client*.config
file
- Much easier than configuring & deploying
- Features of a publisher policy assembly
- This file is a binary/assembly unto itself
- The binary is deployed in the GAC, 'alongside' the shared assembly
- Note: because the assembly is deployed in the GAC the assembly must itself be strongly named!
- The CLR & publisher policy assemblies
- When working with the client app CLR will 1st begin looking in GAC, following the app's manifest
- If, however, the CLR 'sees' a relevant publisher policy binary the CLR will follow the redirection specified in that binary
- Creating a publisher policy assembly
- One still employs XML for the raw content
- Create the binary via the
al.exe
("al" stands for "assembly linker") command-line tool - Must supply the following input parameters:
- Location of the
*.config
or*.xml
file which stores the redirection info - Name you want to give the resulting binary
- Location of the
*.snk
file used to sign the publisher policy assembly - Version number(s) for the publisher policy assembly
- Location of the
- Syntax/example (in command window must be all on one line):
al /link: MySharedLibPolicy.xml
/out:policy.1.0.MySharedLib.dll
/keyf:C:\MyKeys\MyPubPolKey.snk /v:1.0.0.0 - Note: After creating a publisher policy assembly via the
al.exe
tool it appears that you must still deploy that assembly to the GAC (if so, you of course use thegacutil.exe
tool)
- Disabling publisher policy
- Scenario
- You have 10 client apps relying on version 1.0 of a shared library
- You upgrade shared library to version 2.0
- You deploy a publisher policy assembly to direct all client apps to use version 2.0 of the shared assembly
- However, after doing so 1 of your 10 client apps breaks
- Solution
- You want to 'roll back' that one troublesome client installation to use version 1.0 of the shared library
- Approach
- Add an empty
<publisherPolicy>
element to the client's*.config
file - Inside the element set the
apply
attribute to"no"
- Syntax/example:
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="no" />
closing tags…
- Add an empty
- Scenario
- General
- General
- Consuming
- Miscellaneous
app.config
file topics<codeBase>
element- This element allows you to direct your client app to search for dependent assemblies in arbitrary locations
- If the assembly is located on a remote machine
- The dependent assembly will be copied to the download cache
- This is a specific directory in the GAC
- To view the directory execute this in the command window:
gacutil /ldl
- Strong names
- Not surprisingly, this process will only work if the remote assembly is strongly named
- Note: that said, in certain circumstances you can work with assemblies that lack a strong name
- The assembly must be in a sub-directory of client's app directory
- In effect, though, this is simply an alternative to using the
<privatePath>
element
- Syntax/example:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyLib" publicKeyToken="xyz…" />
<codeBase version="2.0.0.0"
href="file:///C:/SomeFolder/MyLib.dll" />
<!-- alternate…
<codeBase version="2.0.0.0"
href="http://www.SomeSite.com/AssembliesMyLib.dll" />
-->
closing tags… - Note: you want to use
<codeBase>
very judiciously- "If you place assemblies at random locations on your development machine, you are in effect re-creating the system registry (and the related DLL hell)…"(Troelsen & Japikse: 7th Ed., p. 550)
- This is because moving or renaming the the parent folders will cause your client app to catch on fire
System.Configuration
namespace- You can add custom information to your
app.config
file System.Configuration
namespace provides types which allow you to read that data at runtime- Setting up the
app.config
file- Entries go inside
<appSettings>
element - Inside
<appSettings>
employ empty<add>
elements <add>
elements take the following 2 attributes:key
value
- Syntax/example:
<configuration>
<appSettings>
<add key="MyConstant" value="3.14159" />
<add key="MyGf" value="Denise" />
closing tags…
- Entries go inside
- To use in code
- Use
GetValue()
method of aAppSettingsReader
type- 1st parameter: name of key
- 2nd parameter: underlying type of key (obtained via
typeof()
operator)
- Syntax/example:
using System.Configuration;
namespace MyNs
{
public class MyClass
{
public void MyAppSettingReaderMethod()
{
AppSettingsReader ar = new AppSettingsReader();
double piage = (double)ar.GetValue("MyConstant", typeof(double));
string gf = (string)ar.GetValue("MyGf", typeof(string));
// Do something with values…
}
}
}
- Use
- Microsoft documentation
- See docs.microsoft.com > .NET > .NET Framework API Reference > .NET Framework Guide > Development Guide > Configuring Apps
- Note: configuration files are employed in many scenarios
- You can add custom information to your
- Defining custom namespaces
- Type Reflection, Late Binding, & Attribute-Based Programming
- General
- Namespace:
System.Reflection
- Tools
ildasm.exe
&reflector.exe
- Namespace:
- Necessity of type metadata (& a working example)
- General
- .NET very good about supplying metadata on types (classes, enums, etc.)
- A number of .NET technologies rely on ability to discover this kind of metadata at runtime
- Ability to obtain this kind of metada is not unique to .NET
- Again, from within
ildasm.exe
utility you use to look at manifests of .NET assemblies- Every type within a .NET assembly identified with a
TypeDef #n
token (e.g.,TypeDef #1
) TypeRef #n
token is used for types defined in other assemblies- Note: the numbers assigned to
TypeDef #n
&TypeRef #n
only signify the order in which the compiler processed the file
- Every type within a .NET assembly identified with a
- Othen tokens & flags used in the metadata:
TypeDefName
- fully-qualified nameExtends
- fully-qualified name of base type
- Metadata for an enumeration
- Will have a line which roughly will read as:
Extends : [TypeRef] System.Enum
- Each individual enumeration will be identified with a
Field Name
token
- Will have a line which roughly will read as:
- Metadata for a class
- Class itself will be adorned with any number of flags indicating how the class is constructed, e.g.:
[Public]
[Class]
[Abstract]
- Method definitions include details on:
MethodName
ReturnType
Arguments
&Parameters
- Properties
- Note: with automatic properties you will see…
- A private backing field (if your automatic field is
FirstNm
you will see something likeField Name: <FirstNm>k__BackingField
) - For a read/write property 2 compiler-generated methods, such as:
get_FirstNm
set_FirstNm
- Lastly, the property definition itself, which will encapsulate
Getter
&Setter
tokens
- A private backing field (if your automatic field is
- Class itself will be adorned with any number of flags indicating how the class is constructed, e.g.:
TypeRef
blocks- There will be one for every externally-referenced type
- Note: Any time you wind up with an
Extends
token in your metadata you will have a correspondingTypeRef
block - Example:
- You define a custom enumeration in your assembly (as noted above, the metadata block for which will note that this extends
System.Enum
) - Your manifest will include a
TypeRef
block, which will include roughlyTypeRefName: System.Enum
- You define a custom enumeration in your assembly (as noted above, the metadata block for which will note that this extends
- How .NET metadata documents…
- The defining assembly
- Defined with
Assembly
block - Contains lots of info, including tokens for:
Name
Public Key
(if appropriate)Major Version, Minor Version,
etc.
- Defined with
- Referenced assemblies
- There is an
AssemblyRef #n
token for each external assembly - At a minimum this block will specify:
Name
(fully-qualified name)Version
- There is an
- String literals
- Each & every one in the assembly is documented in a
User Strings
block - Note: this means you should never store sensitive information as a string literal, as that data can be so easily accessed using
ildasm.exe
(among other methods)
- Each & every one in the assembly is documented in a
- The defining assembly
- General
- Reflection
- Understanding reflection
- General
- Reflection is defined as "the process of runtime type discovery" (Troelsen & Japikse: 7th Ed., p. 561)
- In other words, reflection is the process of obtaining via code anything you can see via
ildasm.exe
- Types within the
System.Reflection
namespace (located inmscorlib.dll
)Assembly
(abstract class)AssemblyName
- used for getting details such as- Version
- Culture
MemberInfo
- Abstract class
- Base class for following abstract classes:
EventInfo
FieldInfo
MethodInfo
PropertyInfo
Module
(abstract class)ParameterInfo
- Not abstract
- Does not derive from
MethodInfo
- The
System.Type
class- Essential for reflection
System.Type
class is essentially giving you the metadata description of a type- Many methods of the class return types from the
System.Reflection
namespace - Properties (all boolean, obviously…)
IsAbstract
IsClass
IsEnum
IsInterface
- others…
- "Get" methods
- Examples:
GetEvents()
GetFields()
GetMembers()
GetMethods()
GetProperties()
- others…
- Notes:
- These types return arrays of "Info" types - i.e.,
GetMethods()
returns an array ofMethodInfo
types, etc. - There are also 'singular' versions of these methods - e.g.,
GetMethod()
will return aMethodInfo
type when passed the name of the method - Most of the overloaded methods allow you to pass in a
BindingFlags
enumeration - allows you to return, say, only static members
- These types return arrays of "Info" types - i.e.,
- Examples:
- Other methods
FindMembers()
- returns an array ofMethodInfo
types based on search criteriaGetType()
- Static method
- Returns a
Type
instance given a string
InvokeMember()
- used for late binding
- Obtaining a reference using
System.Object.GetType()
Type
is an abstract class (ergo, you cannotnew
an instance)- If you have design/compile-time knowledge of your types you can then implement code such as:
Student s = new Student();
Type t = s.GetType();
- Obtaining a reference using
typeof()
- Useful when you do not have, or want to create, an instance of a type
- Syntax/example:
Type t = typeof(Student);
- Notes:
typeof()
expects a strongly-typed name of the type as its parameter- Ergo, this is useful only when you have compile-time knowledge of the type
- Obtaining a reference using
System.Type.GetType()
- This is a static method
- This method takes (at a minimum) the fully-qualified string name of your type
- Ergo, you do not need compile-time knowledge of the
type
- Method has been overloaded to take 2 boolean parameters:
- 1st parameter: specifies whether an exception should be thrown if the type cannot be found
- 2nd parameter: specifies case sensitivity of the string parameter
- Syntax/example # 1:
// When reflecting on a type within
// currently executing assembly…
Type t = Type.GetType("MyNs.Student", false, true); - Syntax/example # 2:
// When reflecting on an external type
// Note: 2nd param is friendly name of external assembly…
Type t = Type.GetType("MyNs.Student, ExtAssmblyNm");
+
token- Used to denote a nested type
- Example:
- Within your student class you have a
Status
enumeration to specify full-time, part-time, etc. - Syntax:
Type t = Type.GetType("MyNs.Student+Status");
- Within your student class you have a
- General
- Code examples
- Reflecting on methods:
static void ListMethods(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
Console.WriteLine($"-> {m.Name}");
} - Reflecting on field names (using LINQ):
static void ListFields(Type t)
{
var fieldNames = from f in t.GetFields() select f.Name;
foreach (var name in fieldNames)
Console.WriteLine($"-> {name}");
} - Returning interfaces
- Code here must be slightly different because an interface is a .NET type unto itself
- Syntax/example:
static void ListInterfaces(Type t)
{
var ifaces = from i in t.GetInterfaces() select i;
foreach (Type i in ifaces)
Console.WriteLine($"-> {i.Name}");
} - Note:
- Within the
foreach
loop we declare the variable to be aType
, not avar
- We obtain the
Name
property inside theforeach
loop, not inside the LINQ query
- Within the
- Using misc. properties & methods:
static void ListVariousStats(Type t)
{
Console.WriteLine($"Base class is: {t.BaseType}");
Console.WriteLine($"Is type abstract? {t.IsAbstract}");
Console.WriteLine($"Is type generic? {t.IsGenericTypeDefinition}");
} - Again, to generate a
Type
use code such as:
Type t = Type.GetType("someTypeName");
- Reflecting on generic types
- Must make use of the back tick character - i.e.,
`
- Following the back tick character you include a number, which is the number of type parameters the generic type supports.
- Example:
List<T>
has (obviously) only 1 type parameter, so to reflect overSystem.Collections.Generic.List<T>
the string you use is as follows…
Type t = Type.GetType("System.Collections.Generic.List`1");
- Example:
Dictionary<TKey, TValue>
has 2 type parameters, so to reflect over it the string you use is as follows…
Type t = Type.GetType("System.Collections.Generic.Dictionary`2");
- Must make use of the back tick character - i.e.,
- Reflecting on method parameters & return values
- You work with
MethodInfo
type for this - In particular,
MethodInfo
itself contains:ReturnType
propertyGetParameters()
method
- Getting
ReturnType
info:
static void GetReturnType(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
Console.WriteLine($"{m.Name} returns {m.ReturnType.FullName}");
} - Getting parameter information:
static void ShowMethodParams(Type t)
{
MethodInfo[] mi = t.GetMethods();
foreach (MethodInfo m in mi)
{
string paramInfo = "( ";
// Get params…
foreach (ParameterInfo pi in m.GetParameters())
{
paramInfo +=
string.Format($"{pi.ParameterType} {pi.Name}");
}
paramInfo += " )";
// Display method signature…
Console.WriteLine($"{retVal} {m.Name} {paramInfo}");
}
} - Leaning on
ToString()
- Has been overridden on the
MethodInfo, PropertyInfo
, etc. types - Displays the signature of the requested item
- The more compact approach:
static void ListMethodsUsingToString(Type t)
{
var methodNames = from n in t.GetMethods() select n;
foreach (var name in methodNames)
Console.WriteLine($"->{name}");
}
- Has been overridden on the
- You work with
- Reflecting on methods:
- Dynamically loading assemblies
- While the above is all well & good, it only allow us to handle assemblies referenced at compile time
- Dynamic loading is "the act of loading external assemblies on demand" (Troelsen & Japikse: 7th Ed., p. 569)
- You use the
Assembly
(abstract) class- Primary methods of interest:
Load()
- requires only friendly name of assemblyLoadFrom()
- You supply full name (i.e., path & extension) of assembly of interest
- Can also read from
<codeBase>
element from yourapp.config
file to retreive path
- Both methods return a
System.Reflection.Assembly
type - These methods allow you to accomplish what you would otherwise rely on client
app.config
file to obtain - A smart developer wraps these methods in
try/catch
blocks in case the searched-for assembly is not found - Syntax/example (of how to obtain info about an assembly):
static void DisplayTypesInAsm(Assembly asm)
{
Console.WriteLine($"->{asm.FullName}");
Type[] types = asm.GetTypes();
foreach (Type t in types)
Console.WriteLine($"Type: {t}");
Console.ReadLine();
} - The
Assembly.GetName()
method- Does not return a
string
- Instead returns an
AssemblyName
type- This type has a number of methods & properties
- Commonly used properties
FullName
Name
Version
- Does not return a
- Primary methods of interest:
- Reflecting on shared assemblies
Assembly.Load()
has been overloaded multiple times- Can pass in a number of parameters, such as
- Version number format: major.minor.build.revision
- Public key token
- Display name
- All of the items which collectively identify an assembly
- Format
- Comma-delimited name/value pairs
- Begins with friendly name of the assembly
- Optional qualifiers follow in parentheses
- Example:
MyLibr (,Version = 2.1.7.2) (,Culture = English) (,PublicKeyToken=blahblahblah)
- Notes:
- When specifying the
PublicKeyToken
value omit all embedded spaces found inildasm.exe
PublicKeyToken=null
indicates you must bind against a non-strongly named assemblyCulture=""
means you use the default culture of target machine- Other than friendly name, order of parameters is immaterial
- When specifying the
- Syntax/example (loading a shared assembly):
Assembly.Load(@"MyLibrary, Version=1.0.0.3,
emsp;PublicKeyToken="null", Culture="""); - Generally, though, it is much easier to avail yourself of the
AssemblyName
type- "Typically, this class is used in conjuntion with
System.Version
, which is an OO wrapper around an assembly's version number." (Troelsen: 5th Ed., p. 599) - An
AssemblyName
instance can then be passed intoAssembly.Load()
- Syntax/example:
// Use AssemblyName to define display name…
AssemblyName asName = new AssemblyName();
asName.Name = "MyLibrary";
Version v = new Version("1.1.2.0");
asName.Version = v;
Assembly a = Assembly.Load(asName); - Note: when working with this myself I had trouble with
CultureInfo
property- You need to new a
CultureInfo
type - e.g.,myAsmName.CultureInfo = new CultureInfo("en-US");
- MSDN has no guidance for leaving this blank
- You need to new a
- Passing in a public key token
- There is no corresponding property
- Most appropriate property is the
KeyPair
property, which requires you to point to a*.snk
file
- "Typically, this class is used in conjuntion with
- Syntax/example (obtaining desired info on loaded assembly):
private static void DisplayInfo(Assembly a)
{
Console.WriteLine("Loaded from GAC? {0}", a.GlobalAssemblyCache);
// Note uses of GetName()…
Console.WriteLine("Asm Name: {0}", a.GetName().Name);
Console.WriteLine("Asm Version: {0}", a.GetName().Version);
Console.WriteLine("Asm Culture: {0}",
a.GetName().CultureInfo.DisplayName);
}
- Understanding reflection
- Late binding
- General
- Late binding is creating an instance of a type (in order to invoke its members) at runtime
- Typically means you do not have compile-time knowledge of type's existence
- Implications
- You have not set a reference to the type in your project
- Caller's manifest has no reference to the assembly
- Major rationale for late binding is to allow you to build extendable apps
- Basic steps
- Define an
Assembly
instance, representing your assembly of instance - Invoke
GetType()
on your assembly, to obtain the type (from within the assembly) in which you are interested - Pass that
Type
intoActivator.CreateInstance()
to get an instance - Put reflection to work on the returned
System.Object
- Define an
- The
System.Activator
class- The base type used for late binding
- Defined in
mscorlib.dll
CreateInstance()
is main method used for late binding- Method is oveloaded, but easiest version takes a
Type
object - Limitation with late binding
Activator.CreateInstance()
does not return a strongly-typed instance- Instead, it returns a
System.Object
- Further, you cannot cast the retruned
object
to your desired type- The whole point of late binding is working with a type for which you have no knowledge at either design or compile time
- This means that there is no reference to the appropriate library in your project
- Syntax/example, using Troelsen & Japikse's
Car
example (7th Ed., pp. 574‑5):
static void Main(string[] args)
{
// Try to load local copy of Car library…
Assembly a = null;
try
{
a = Assembly.Load("CarLibrary");
}
catch(FileNotFoundException ex)
{
Console.WriteLine(ex.Message);
return;
}
if (a != null)
CreateUsingLateBinding(a);
}
static void CreateUsingLateBinding(Assembly asm)
{
// Try-catch blocks omitted…
// Get metadata for the MiniVan type…
Type miniVan = asm.GetType("CarLibrary.Minivan");
// Create the minivan on the fly…
object obj = Activator.CreateInstance(miniVan);
} - You then use reflection to obtain & invoke the methods of your returned
System.Object
- Invoking methods with no parameters
- To get a hold of a method from your returned
System.Object
- Use
Type.GetMethod()
, which returns aMethodInfo
instance - Invoke
Invoke()
(!) on thatMethodInfo
instance
- Use
MethodInfo.Invoke()
takes 2 parameters- The object on which to invoke the method (of type
System.Object
) - An array of
System.Object
s, which serve as the method's parameters
- The object on which to invoke the method (of type
- If the method takes no parameters simply pass in
null
as the 2nd parameter toMethodInfo.Invoke()
- Syntax/example, again using
Car
example from Troelsen & Japikse (7th Ed., pp. 576‑7 )
// Same as above…
Type miniVan = asm.GetType("CarLibrary.Minivan");
object obj = Activator.CreateInstance(miniVan);
// Get info for TurboBoost…
MethodInfo mi = miniVan.GetMethod("TurboBoost");
// Invoke method (TurboBoost takes no parameters)…
mi.Invoke(obj, null);
- To get a hold of a method from your returned
- Invoking methods with parameters
- Again leaning on the
car
example (Troelsen & Japikse: 7th Ed., pp. 577‑8), theTurnOnRadio()
method takes 2 params:- A
bool
controlling whether to turn the radio on or off - An
enum
controlling whether to use radio, CD, tape, etc.Enums
, of course, have underlying numerical values- Therefore, you can pass in a numerical value as your parameter rather than pass in an
enum
- A
- Syntax/example (omitting try/catch blocks):
static void InvokeMethodsWithArgsUsingLateBinding(Assembly asm)
{
// Get metadata description of sports car…
Type sport = asm.GetType("CarLibrary.SportsCar");
// Create the sports car…
Object obj = Activator.CreateInstance(sport);
// Invoke TurnOnRadio with args…
MethodInfo mi = sport.GetMethod("TurnOnRadio");
mi.Invoke(obj, new object[] { true, 2 });
}
- Again leaning on the
- General
- Attributes
- .NET attributes
- General
- Attributes are a way to embed additional information in assembly metadata
- Essentially nothing more than code annotations
- Concept is not unique (or new) to .NET
- Can be applied to any
- Module
- Assembly
- Type (class, interface, etc.)
- Type member
- .NET attributes extend
System.Attribute
base class - Inherit from this class to build your own, custom attributes
- Sampling of predefined attributes
[CLSCompliant]
- tells compiler to check for (& thus enforce) CLS conformity of item attributed[DllImport]
- "Allows .NET code to make calls to any unmanaged C- or C++-based code library" (Troelsen & Japikse: 7th Ed., p. 605)
- This includes calls to underlying operating system
- Do not use to communicate with COM-based code
[Obsolete]
- generates a compiler warning to any user/consumer of the attributed item[Serializable]
- Usable on classes & structures
- Means that the item perists its current state into a stream
[Nonserialized]
- i.e., do not persist item's current state into a stream[WebMethod]
- Used to flag a method as being invokable via an HTTP request
- Also tells CLR to serialize any method return values as XML
- Attribute consumers
- Until & unless some software reflects over the information in attributes that information is simply harmless excess entries in your compiled assembly
- CLR compiler itself looks for attributes such as
[CLSCompliant]
[Obsolete]
- Particular methods in various .NET classes also look for individual attributes
- Can create custom methods which look for custom attributes
- Applying attributes in C#
- Attributes show up in compiled metadata
- Can apply multiple attributes to a type
- Syntax/example - can put (comma-separated) multiple attributes with single set of square brackets:
[Serializable, Obsolete([Are you kidding?")]
public class Pager
{ // Implementation code… } - Syntax/example - can 'stack' multiple:
[Serializable]
[Obsolete([Are you kidding?")]
public class Pager
{ // Implementation code… } - Note: either syntax achieves exactly the same results
- Syntax/example - can put (comma-separated) multiple attributes with single set of square brackets:
- C# shorthand notation
- All .NET attributes carry a suffix of
Attribute
- In other words, the actual name of the
Obsolete
attribute isObsoleteAttribute
- C# allows you to omit that
Attribute
suffix - Notes:
- Any custom attributes you create should also carry the
Attribute
suffix - Not all .NET languages allow you to omit the
Attribute
suffix when applying an attribute
- Any custom attributes you create should also carry the
- All .NET attributes carry a suffix of
- Specifying constructor parameters for attributes
- As already seen with
[Obsolete]
attributes can take constructor parameters - In fact, the
[Obsolete]
attribute has 3 constructors - As with any custom class you can supply any number of constructors to your custom attributes
- When you do create parameterized constructors for custom attributes
- Your attribute is not allocated into memory until the parameters are targeted via reflection from
- Another type
- Some external tool
- Instead, "the string data defined at the attribute level is simply stored within the assembly as a blurb of metadata" (Troelsen & Japikse: 7th Ed., p. 608)
- Your attribute is not allocated into memory until the parameters are targeted via reflection from
- As already seen with
- The
Obsolete
attribute- The string passed into the constructor shows up as a warning in the error window upon compiling a class so attributed
- The message also shows up when you hover your cursor over any instantiated instance of the class
- General
- Building custom attributes
- General
- Again, derive from
System.Attribute
- .NET Best Practice: define all custom attribute classes as
sealed
- Syntax/example:
public sealed class SomeCustomFeatureAttribute : System.Attribute
{
public string Description { get; set; }
// Ctors…
public SomeCustomFeatureAttribute(string descrip)
{ Description = descrip; }
public SomeCustomFeatureAttribute() { }
} - Custom attributes are applied to classes, methods, etc., just as you do with built-in .NET attributes
- Again, derive from
- Named property syntax
- So far have passed values into attributes simply via their constructors
- With named property syntax can also pass in values via any public, writable properties
- Syntax/example:
[Serializable]
[SomeCustomFeature(Description = "My little class")]
public class MyClass
{
}
- Restricting attribute usage
- As it stands custom attributes you create can be applied to any part of your code - classes, methods, enums, etc.
- This, however, may not be the range of usage you want to allow
- Solution: apply
[AttributeUsage]
attribute to your custom attribute- Defining class contains an
enum
calledAttribute Targets
xx
< xx >
- Values:
All
Assembly
Class
Constructor
Field
Method
- Others
- Use the pipe symbol (
¦
) to combine enum values on an "or" basis
- Values:
- Attribute also has a couple of boolean named properties
AllowMultiple
- specifies whether the attribute can apply to the same item more than once (how the hell would that work???)Inherited
- whether attributes apply to derived classes
- Syntax/example:
// Apply AttributeUsage to a custom attribute…
[AttributeUsage(AttributeTargets.Class ¦ AttributeTargets.Struct,
Inherited = true[
public sealed class MyDescriptionAttribute : System.Attribute
{ // Implementation code… }
- Defining class contains an
- General
- Assembly-level (and module-level) attributes
- General
- These attributes apply to all types within a targeted assembly (or module)
- Accomplish this via use of the 'tags'
- [module:]
- [assembly:]
- Troelsen strongly encourages use of
CLSCompliant
attribute in this manner, especially if there is any change of your assembly being consumed by any types written in other .NET languages (Troelsen & Japikse: 7th Ed., pp. 612-3) - Syntax/example:
// Place 'using' statements first…
using System;
using System.Collections.Generic;
// etc. …
// Next place assembly- or module-level attributes…
[assembly: CLSCompliant(true)]
// Namespace and types are next…
namespace MyNS
{
// Etc., etc., etc. …
}
- The VS 2010
AssemblyInfo.cs
file- By default, VS creates a file called
AssemblyInfo.cs
for all new projects - Access file via
- Opltimal place for assembly-level attributes
- May well find attributes already there (values of which are, then, already in the assembly-level metadata):
[AssemblyCompany]
[AssemblyCopyright]
[AssemblyDescription]
[AssemblyVersion]
- others…
- By default, VS creates a file called
- General
- Reflecting on attributes
- Using early binding
- First, nothing can happen with a custom attribute unless a client app or type has compile-time knowledge of that attribute
- Thus, you need to set a reference to your custom attribute class in your client app
- Next
- Get a
type
reference to your type (usually class) of interest - Invoke
Type.GetCustomAttributes()
on thattype
instance- The method takes a boolean parameter, which controls whether to return custom attributes up the type's inheritance chaing
- The method returns an array of
object
s
- Get a
- Syntax/example (directly from Troelsen & Japikse: 7th Ed., pp. 614-5):
// Include standard 'using' statements, then add…
using AttributedCarLibrary;
namespace VehicleDescriptionAttributeReader
{
class Program
{
static void Main(string[] args)
{
ReflectOnAttributesUsingEarlyBinding();
Console.ReadLine();
}
private static void ReflectOnAttributesUsingEarlyBinding()
{
// Get a type representing the Winnebago…
Type t = typeof(Winnebago);
// Get all attributes of the Winnebago…
object[] customAtts = t.GetCustomAttributes(false);
// Print the description…
foreach (VehicleDescriptionAttribute v in customAtts)
Console.WriteLine("-> {0}\n", v.Description);
}
}
}
- Using late binding
- General approach
- Creating an
Assembly
instance - Getting
Type
references- The class defining the custom attribute
- An array of
Types
, by invokingGetTypes()
on theAssembly
instance
- Assuming you have defined a public property in your custom attribute, get a
PropertyInfo
instance to capture that type - Lastly, invoke
GetValue()
on that property (passing in the appropriate parameters) to hit the property' accessor
- Creating an
- Syntax/example (from Troelsen & Japikse: 7th Ed., p. 616, try/catch blocks omitted):
private static void RefelctAttributesUsingLateBinding()
{
// Load the local copy of AttributedCarLibrary…
Assembly asm = Assembly.Load("AttributedCarLibrary");
// Get type info of the VehicleDescriptionAttribute…
Type vehicleDesc =
asm.GetType("AttributedCarLibrary
.VehicleDescriptionAttribute");
// Get type info of the Description property…
PropertyInfo propDesc = vehicleDesc.GetProperty("Description");
// Get all types in the assembly…
Type[] types = asm.GetTypes();
// Iterate over each type & obtain any VehicleDescriptionAttributes…
foreach (Type t in types)
{
object[] objs = t.GetCustomAttributes(vehicleDesc, false);
// Iterate over each VehicleDescriptionAttribute and print
// the description using late binding…
foreach (object o in objs)
{
Console.WriteLine("-> {0}: {1}\n",
t.Name, propDesc.GetValue(o,null));
}
}
}
- General approach
- Using early binding
- .NET attributes
- Building an extendable application
- General
- This is where you build your app so that other assemblies can be referenced that will add functionality
- Those added-in assemblies are called snap-ins
- See Troelsen & Japikse: 7th Ed., pp. 618-24, for full example
- Building
CommonSnappableTypes.dll
- Snap-ins only work if you provide an interface for them to adhere to
- While conceivably this could be an abstract class, Troelsen's example uses an interface
- Interface is declared in this external assembly
- The assembly also defines an attribute (i.e., a class inheriting from
System.Attribute
- The attribute is to be used by any class that wants to be snapped-in
- Building the snap-ins
- Troelsen's example has 2 assemblies, 1 written in C# & 1 in VB.Net
- Each snap-in contains a single class, which
- Implements the above-mentioned interface
- Employs the custom attribute
- As such, each assembly contains a reference to the CommonSnappableTypes dll
- Note re: implementing the interface
- Troelsen elects to implement the interface explicitly
- Logic is that the snapped-into app is the only thing which you really want to interact with the methods, etc. defined by the interface
- Implicit implementation would more readily expose the methods, etc., defined by the interface
- Building an extendable Windows Form application
- Assembly references
- You must include a reference to your
CommonSnappableTypes
-like assembly - In practice, however, you do not have design or compile-time knowledge of the snap-ins
- You must include a reference to your
- Snapping in the snap-ins
- Troelsen's example generates a
OpenFileDialog
instance & has user navigate to snap-in dll - Once the user opens the file you then must write code which
- Loads the assembly into memory, via
Assembly.LoadFrom()
- Looks for any & all classes implementing the interface(s) defined in the CommonSnappableTypes-like assembly
- Use LINQ for this, working on the just-created
Assembly
instance - Syntax/example:
// Get all IAppFunctionality compatible classes in assembly…
var theClassTypes = from t in theSnapInAsm.GetTypes()
where t.IsClass &&
(t.GetInterface("IAppFunctionality") != null)
select t;
- Use LINQ for this, working on the just-created
- Uses late binding to create the desired type, &, as necessary, invoke methods
- Use
foreach
to loop through the just-created assembly, invoking desired methods - Syntax/example:
// Now, create the object and call DoIt() method…
foreach (Type t in theClassTypes)
{
// Use late binding to create the type…
IAppFunctionality itfApp =
(IAppFunctionality)theSnapInAsm.CreateInstance(t.FullName, true);
itfApp.DoIt();
}
- Use
- Loads the assembly into memory, via
- Lastly, you can also handle custom attributes
- Within the just-described
foreach
loop you can pass the type to a helper method - Syntax/example:
private void DisplayCompayData(Type t)
{
// Get [CompanyInfo] data…
var compInfo = from ci in t.GetCustomAttributes(false)
where
(ci.GetType() == typeof(CompanyInfoAttribute))
select ci;
// Show data…
foreach (CompanyInfoAttribute c in compInfo)
{ // Do something with the custom attribute… }
}
- Within the just-described
- Troelsen's example generates a
- Assembly references
- General
- General
- Building & Configuring Class Libraries
- .NET Base Class Libraries
- Multithreaded & Parallel Programming
- The Process/AppDomain/Context/Thread relationship
- General
- Will be working within
System.Threading
namespace - To obtain a reference to the current thread:
Thread currThread = Thread.CurrentThread;
- AppDomains & threads
- Not a 1-to-1 correspondence between them
- In fact, an AppDomain can have multiple threads executing at once
- Further, a thread is not confined to a given AppDomain
- That said, a given thread can only operate within a given AppDomain at any one time
- To get the AppDomain hosting the current thread:
AppDomain ad = Thread.GetDomain();
- Contexts & threads
- Threads can be moved among contexts at the whim of the CLR
- To get the context within which a thread is executing:
Context ctx = Thread.CurrentContext;
- Note: a context is a
System.Runtime.Remoting.Contexts.Context
object
- As a rule you do not have to worry about where a thread is executing
- Will be working within
- The problem of concurrency
- Your code has little control over how the OS or CLR uses its/their threads
- Threads aren't even guaranteed to run immediately upon creation
- Applications & threads
- Thread-volatile operations are those portions of your app subject to multithreaded access
- Atomic operations are immune to multithreaded access
- Thread-volatile operations are obviously the risky ones
- The risk with concurrency
- Because you have no control over when a thread stops its work it is always possible in multithreaded programs that thread #1 begins altering an object but is halted by the CLR or OS before finishing its work
- If thread #2 then accesses that same object it could be operating on that object in a 'corrupt' state
- This leads to virtually impossible-to-replicate - & weird - bugs
- Unfortunately, very few .NET operations are guaranteed to be atomic
- Thread synchronization
- Must use a number of .NET primitives to deal with thread volatility, e.g.:
- Locks
- Monitors
[Synchronization]
attribute
- Best approach is the newest: using the
async
&await
keywords - However, existing code bases are shot through with async delegates
- Must use a number of .NET primitives to deal with thread volatility, e.g.:
- General
- Some threading primitives
- The
MarshalByRefObject
class- Abstract
- "Enables access to objects across application domain boundaries in applications that support remoting." docs.microsoft.com > .NET > .NET Framework API Reference > System > MarshalByRefObject
- This ability is critical for threading
- For apps to communicate across app domains must either:
- Send copies of objects between each other
- Exchange messages via proxy, which is what
MarshalByRefObject
enables
- Has only its default constructor
- None of the methods seems to be used by
WaitHandle
class
- The
WaitHandle
class- General
- Derives from
MarshalByRefObject
- Like
MarshalByRefObject
, also abstract - Implements
IDisposable
- Parent to other, non-abstract threading primitives (discussed below)
- Constitutes a wrapper around OS syncronization handles
- Synchronization handles are those objects (i.e., threads) which need to wait for (exclusive) access to shared resources
- An OS typically offers multiple wait operations on these objects
WaitHandle
is essentially the (abstract) air traffic controller of multi-threading
- Signaling (aka Thread interaction)
- With multi-threading you can have several threads waiting to fire and/or several threads being waited upon
- In a multi-threaded environment threads obviously need to communicate in order to determine which ones can proceed & which ones must wait
- 2 mechanisms for one or more threads to notify some other thread that that (other) thread can proceed
- Thread A blocks itself from proceeding until after Thread B by invoking Thread B's
Thread.Join
method - If Thread A has acquired a lock Thread B cannot proceed until both:
- Thread A releases that lock
- Thread B in turn acquires the lock
- Thread A blocks itself from proceeding until after Thread B by invoking Thread B's
- Signaling state
- Unsignaled state equivalent to
false
(& signaled totrue
) - A state of 'signaled' means that threads are free to proceed
- Unsignaled state equivalent to
- Derives from
- Notable methods
Close()
- releases all held resourcesSignalAndWait()
- Overridden several times
- Sends a signal to one
WaitHandle
-derived object while it waits on anotherWaitHandle
-derived object - Can pass in a time-out parameter
WaitAll()
&WaitAny()
- Both are static members
- Specify whether the calling thread must wait for all or just one called thread to signal
- Both are overridden several times - most significantly to specify a time limit on how long to wait before signaling
- Both take an array of (other)
WaitHandle
-derived objects as a parameter
WaitOne()
- Virtual method
- Overridden several times - most notably (like
WaitAll()
&WaitAny()
) to accept a maximum time period to wait before returningtrue
(i.e., signaling)
- Derived classes
- The
AutoResetEvent
&ManualResetEvent
- Both are sealed
- Both used to block &/or release threads
- Signaling state must be specified in the constructors of both 'events'
- Both receive signals indicating that a previously-working thread has finished its duties
- The
AutoResetEvent
- Analagous to a toll booth
- Upon releasing a signal it releases a single thread, then resets itself
- The
ManualResetEvent
- Analagous to a barn door
- After being signaled
ManualResetEvent
will remain signaled until some thread changes its signaled status - Methods (only 2)
Set()
- changes state to signaled, which releases all held threadsReset()
- changes state of the event to nonsignaled - i.e., blocks other threads
- The
- Typically, one uses inherited (from
WaitHandle
) methods likeWaitOne(), WaitAll()
, etc. on the instances of these types
- The
EventWaitHandle
class- This is the 3rd class one uses for thread interaction/signaling
- Encapsulates a thread synchronization event
- Constructor parameters
initialState
(type:bool
) to indicated whether or not theEventWaitHandle
instance is in a signaled statemode
- A
System.Threading.EventResetMode
enum - Values are
AutoReset
&ManualReset
- A
- Notes
- After instantiation effectively the same as either
AutoResetEvent
orManualResetEvent
- One difference:
EventWaitHandle
class gives you access to named synchronization events
- After instantiation effectively the same as either
- Notable methods (non-inherited)
OpenExisting()
- Overloaded
- Pass in a
string
of named synchronization event
Reset()
- Sets event state to non-signaled
- Blocks waiting thread(s)
Set()
- Sets event state to signaled
- Allows waiting thread(s) to proceed
Mutex
- Implements
IDisposable
- Used to coordinate access to a resource when multiple threads need access to that resouce
- Mechanism
- Typically used in conjuction with
WaitOne()
method - Essentially a token passed among threads
- A thread must "possess" a/the
Mutex
in order to access the shared resource - When a thread is finished with its work it must invoke
ReleaseMutex()
for a a second thread to take possession of saidMutex
- Typically used in conjuction with
- Calling/notification details
- Typically you declare a
Mutex
as a private & static class field - A threads lays claim to a/the
Mutex
by invoking itsWaitAll(), WaitAny()
, or, more commonly,WaitOne(),
methods Mutex
""ownership" is granted on a first-come, first-serve basis- A thread then either releases its/the
Mutex
by:- Invoking
Mutex.ReleaseMutex()
- Having the (optional)
timeout
parameter initially passed into theWaitOne()
invocation elapse
- Invoking
- Typically you declare a
WaitOne()
signaling/return valuestrue
- The mutex is currently foot-loose & fancy free
- Ergo, the thread invoking
WaitOne()
can take ownership of the mutex
false
- Returns
false
when the previous owner had its time elapse - This will actually prevent the calling thread from taking ownership of the mutex
- This in turn means that the new trying-to-take-ownership thread will generate a run-time error if/when it tries to invoke
ReleaseMutex()
- Returns
- Thread identity
- Enforced by
Mutex
- This means that only the thread which acquired the mutex can invoke
ReleaseMutex()
- Enforced by
- An abandoned mutex
- This occurs when a thread terminates while owning a mutex
- At this point the mutex is set to signaled, & another thread can take ownership of the mutex
- However, beginning with .NET 2.0, a thread which acquires an abondoned mutex will immediately throw an
AbandonedMutexException
- Note: an abandoned mutex is considered a howling error in one's code
- Implements
Semaphore
- Somewhat like the
Mutex
, in that threads must obtain a semaphore in order to operate on a shared resource - However…
- Allows a pool of threads to access a resource simultaneously
- Maximum number of threads is set in the constructor
Semaphore.Release()
method- Invoked by a thread to release a semaphore from its ownership
- Note: a semaphore does not have thread affiinity - ergo, Thread B can cause Thread A to release a semaphore
- Somewhat like the
- The
- Initializing a
WaitHandle
object- It is the type of the
IAsyncResult.AsyncWaitHandle
property - Can new one or an array of
WaitHandle
object(s)
- It is the type of the
- General
- The
- Delegates
- Asynchronous nature of delegates
- The
BeginInvoke()
method- Parameters (in order):
- Whatever parameters are required to be passed into the pointed-to method
- Parameter of type
AsyncCallback
(usually namedcb
) - Parameter of type
object
(usually namedstate
) - Note: can pass in
null
for last 2 params
- Must return an
IAsyncResult
- Parameters (in order):
- The
System.IAsyncResult
interfaceEndInvoke()
takes aSystem.IAsyncResult
as its sole parameter- Definition
public interface IAsyncResult
{
object AsyncState { get; }
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted{ get; }
} - Handling
- Cache the
IAsyncResult
result when you fireBeginInvoke()
- Pass the
IAsyncResult
object toEndInvoke()
when you are ready for the results - In simple cases you never need to work with the
IAsyncResult
object's properties
- Cache the
- Methods returning
void
- No need to cache the
IAsyncResult
- No need to ever fire
EndInvoke()
, since there is noIAsyncResult
object to be passed into the method
- No need to cache the
- The
- Using
IAsyncResult
to invoke a method asynchronously- Approach 1:
- Utilize the
IsCompleted
ppty ofIAsyncResult
- Allows calling thread to keep working while the called method slowly churns away
- Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 701‑3):
namespace SyncDelegateReview
{
public delegate int BinaryOp(int x, int y);
class Program
{
static void Main(string[] args)
{
BinaryOp b = new BinaryOp(Add);
// Invoke Add() on a secondary thread…
IAsyncResult itfAR = b.BeginInvoke(10, 10, null, null);
while (!itfAR.IsCompleted)
{
Console.WriteLine("Doing more work in Main()");
Thread.Sleep(1000);
}
// Now that Add() is finished get result…
int answer = b.EndInvoke(itfAR);
// Do something with answer…
}
static int Add(int x, int y)
{
// Pause to simulate a lengthy operation…
Thread.Sleep(5000);
return x + y;
}
}
}
- Utilize the
- Approach 2:
- Use the
IAsyncResult.AsynchWaitHandle
property - That property is of type
WaitHandle
- Use the
WaitHandle
-derived object's (overloaded)WaitOne()
method- First parameter of
WaitOne()
is maximum wait time (in milliseconds) WaitOne()
returns false once the maximum wait time is exceeded
- First parameter of
- Use the
- All in all, neither the
IsCompleted
property nor theAsyncWaitHandle.WaitOne()
method is the best way to handle asynchronous method calls
- Approach 1:
- Using the
AsyncCallback
delegate for asynchronous method invocation- We've ignored this so far (by supplying a
null
value) - Alternative to above approach of, effectively, continually polling called method to see when it is done
- "However, when you do supply an
AsyncCallback
object, the delegate will call the specified method automatically when the asynchronous call has completed." (Troelsen & Japikse: 7th Ed., p. 703) - Notes:
- The callback method will run on the secondary, not primary, thread
- This becomes important when working with GUIs such as WPF or Windows Forms
- Targets of the delegate must:
- Return
void
- Take a single,
IAsyncResult
parameter
- Return
- Syntax/example (condensed from Troelsen & Japikse: 7th Ed., pp. 704‑5):
class Program
{
private static bool isDone = false;
static void Main(string[] args)
{
BinaryOp b = new BinaryOp(Add);
IAsyncResult itfAR = b.BeginInvoke(10, 10,
new AsyncCallback(InvokedMethodComplete), null);
while(!isDone)
{ // Assume other work is performed here… }
int answer = b.EndInvoke(itfAR);
}
static int Add(int x, int y)
{ return x + y; }
static void InvokedMethodComplete(IAsyncResult itfAR)
{ isDone = true; }
} - The
AsyncResult
class- Drawback of the above architecture
- You often want to capture the result of your called procedure (e.g.,
Add()
) within the method pointed to by theAsyncCallback
delegate - To enable accessing that result the method to which the
AsyncCallback
delegate points would need to be able to:- Call
EndInvoke()
on the original instance of the asynchronously-called delegate - And, even before that, get the
AsyncCallback
delegate's hands on the original instance itself of the asynchronously-called delegate (i.e., theBinaryOp
delegate instance,b
)
- Call
- You often want to capture the result of your called procedure (e.g.,
- One solution: declare the intial asynchronously-called delegate variable as a static member of the parent class
- Better solution - Inside the method pointed to by the
AsyncCallBack
delegate:- Cast the
IAsyncResult
parameter as anAsyncResult
type - Note:
AsyncResult
is found in theSystem.Runtime.Remoting.Messaging
namespace - So long as you perform the proper casting, the
AsyncDelegate
property of theAsyncResult
class will return a reference to the original, asynchronous delegate on which you calledBeginInvoke()
- At that point you:
- Call
EndInvoke()
on this just (re-)created instance of the method you called asyncronously - Handle the result(s) of
yourDelegateInstance.EndInvoke()
however you please - Note: remember to pass in the
IAsyncResult
to theEndInvoke()
method
- Call
- Syntax/example (
InvokedMethodComplete()
modified from above):
static void InvokedMethodComplete(IAsyncResult itfAR)
{
Console.WriteLine("Our addition is complete.");
// New get the result inside this method…
AsyncResult ar = (AsyncResult)itfAR;
// Cool, eh?…
BinaryOp b = (BinaryOp)ar.AsyncDelegate;
int answer = b.EndInvoke(itfAR);
isDone = true;
}
- Cast the
- Drawback of the above architecture
- Passing & receiving custom state data
- You use the final parameter of
BeginInvoke()
- That parameter is of type
System.Object
, so you can pass in anything (e.g., astring
) - To use your custom data from within the method to which your
AsyncCallback
delegate is pointing:- Use the
AsyncState
property of theIAsyncResult
parameter - You need to cast that
AsyncState
property to the appropriate type - Note: this means the calling & called methods must agree on that underlying type of the
state
param - Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 706‑7)
static void Main(string[] args)
{
// other code…
IAsyncResult itfAR = b.BeginInvoke(10, 10,
new AsyncCallback(InvokedMethodComplete),
"Message from Main()");
// more code…
}
static void InvokedMethodComplete(IAsyncResult itfAR)
{
// other code…
string msg = (string)itfAR.AsyncState;
// do something with msg variable…
isDone = true;
}
- Use the
- You use the final parameter of
- We've ignored this so far (by supplying a
- Asynchronous nature of delegates
- The
System.Threading
namespace- Contains types for:
- Manipulating threads
- Accessing thread pool maintained by CLR
- A non-GUI-based
Timer
class - Synchronizing access to shared resources
- Major types
Interlocked
- "provides atomic operations for variables that are shared by multiple threads" (Troelsen & Japikse: 7th Ed., p. 708)Monitor
- Uses locks & wait/signals to synchronize threading objects
- Used by the
lock
keyword behind the scenes
Mutex
- for synchronizing between app domain boundariesParameterizedThreadStart
- "This delegate allows a thread to call methods that take any number of arguments." (Troelsen & Japikse: 7th Ed., p. 708)Semaphore
- use to limit the number of threads which can concurrently access a resourceThread
- can use to father additional threads in the original AppDomainThreadPool
- gives you access to the thread pool (managed by CLR) within a given processThreadPriority
- anenum
ThreadStart
- A delegate
- Points to the method to call for a particular thread
- Targets must have the same signature (unlike the
ParamaterizedThreadStart
delegate)
ThreadState
- anenum
Timer
- use to fire a method at specific intervalsTimerCallback
- a delegate, used in conjuntion withTimer
types
- Contains types for:
- The
System.Threading.Thread
class- General
- "This class represents an object-oriented wrapper around a given path of execution within a particular AppDomain." (Troelsen & Japikse: 7th Ed., p. 708)
- Use this to create, suspend, stop, & destroy threads
- Static members
CurrentContext
- read-onlyCurrentThread
- read-onlyGetDomain()
&GetDomainID()
- refers to current AppDomainSleep()
- suspends a thread for indicated period of time (miliseconds)
- Instance-level members
IsAlive
- i.e., started & not yet terminated or abortedIsBackground
Name
- use to give a thread a friendly namePriority
- returns aThreadPriority
enumerationThreadState
- returns aThreadState
enumerationAbort()
- terminates said thread ASAPInterrupt()
- awakens a thread from a wait periodJoin()
- blocks a calling thread until the thread on whichJoin()
is called exitsResume()
- use on a previously suspended threadStart
- fire up the thread ASAPSuspend()
- CLR ignores this if the thread is already suspended
- Notes re: aborting or suspending an active thread
- Generally considered jolly bad form, old boy
- Though small, there is a risk that the thread in question could "leak" its workload
- The
Name
property- Setting this helps enormously when debugging
- While debugging can access in Threads window via
- The
Priority
property- Surpise, surpise, the default
Priority
property isNormal
- There are 5 total values in the
System.Threading.ThreadPriority
enum - Setting a priority level
- Does not give you control over when the CLR thread scheduler switches between threads
- Think of priority level as a gentle hint to the thread scheduler
- One generally does not monkey with this property
- Surpise, surpise, the default
- General
- Programming with threads
- Programmatically creating secondary threads
- Steps for
- 1) Create/identify the method where the new thread will start (i.e., entry point)
- 2) Create a new
ThreadStart
(orParameterizedThreadStart)
delegate, passing in the name of your target method - 3) Passing in the just-created delegate to the constructor, instantiate a new
Thread
object - 4) Set any desired properties on the thread (e.g.,
Name
) - 5) Step back from the launching pad, & invoke
Start()
on the new thread object/variable
- The
ThreadStart
delegate- Can only point to methods that take no parameters & return
void
- Syntax/example:
static void Main(string[] args)
{
// Name the current thread…
Thread primaryThread = Thread.CurrentThread;
primaryThread.Name = "Primary";
// Instantiate worker class object…
SomeClass sc = new SomeClass();
// Make the thread…
Thread backgroundThread =
new Thread(new ThreadStart(sc.SomeSlowMethod));
backgroundThread.Name = "Secondary";
backgroundThread.Start();
// Resume work (on primary thread)…
}
- Can only point to methods that take no parameters & return
- The
ParameterizedThreadStart
delegate- Create a simple, custom class to hold all of the parameters that you will need to pass
- Define a custom constructor for the class to populate the public fields
- Inside the method you want to invoke asynchronously:
- Define the method so that it takes a single parameter of type
object
- Wrap the operative code inside an
if(myOneParam is MyCustomDataClass)
statement - Cast the incoming
object
parameter to the custom class type - Access the underlying parameters you need as properties/public fields of the custom class type
- Define the method so that it takes a single parameter of type
- The
AutoResetEvent
class- A better tack when you need thread #1 to wait until thread #2 is done
- Issues with above approaches
- When using asynchronous delegates you run the risk of multiple threads accessing the same object
- You eliminate this risk by using
Thread.Sleep()
, but you are probably encoding a longer-than-necessary wait
- Steps for using
AutoResetEvent
class- Create a static instance of
AutoResetEvent
inside the class which needs to wait - Pass in
false
as a constructor parameter (this indicates that you have not yet received the required notification for your code to proceed) - Fire the
WaitOne
method wherever you want your code to twiddle its thumbs - At the point in the called procedure where the calling method can stop waiting, invoke
myAutoResetEventInstance.Set();
- Create a static instance of
- Syntax/example (adapted from Troelsen & Japikse: 7th Ed., pp. 715‑6 ):
class Program
{
private static AutoResetEvent waitHandle =
new AutoResetEvent(false);
static void Main(string[] args)
{
// Our not-shown custom parameter-holding object…
AddParams ap = new AddParams(10, 10);
Thread t = new Thread(new ParameterizedThreadStart(Add));
t.Start(ap);
// Wait here until notified…
waitHandle.WaitOne();
// Nothing below will fire until
// waitHandle.Set() is invoked…
}
static void Add(object data)
{
if(data is AddParams)
{
AddParams ap = (AddParams)data;
int sum = ap.a + ap.b;
// Tell the other thread we are done…
waitHandle.Set();
}
}
}
- Foreground & background threads
- Foreground threads
- Presence effectively prevent current app from shutting down
- CLR will not terminate an app - i.e., the hosting AppDomain - until all foregrounds threads have finished their work
- Background threads (aka daemon threads)
- CLR views them as expendable, even when in the middle of work
- Automatically killed when app domain unloads
- "…foreground and background threads are not synonymous with primary and worker threads." (Troelsen & Japikse: 7th Ed., p. 716)
Thread.Start()
- Any thread started in this manner is a foreground thread by default
- Ergo, app domain will not unload if any thread started in this manner is still alive
- To create a thread as a background thread you need to set
IsBackground
ppty totrue
- Create a background thread for non-critical work (e.g., periodically checking emails)
- Foreground threads
- Steps for
- The issue of concurrency
- General
- All threads in AppDomain can access shared data
- However, you need to protect against multiple threads changing shared data
- Synchronization using the C#
lock
keyword- This prevents thread #2 from interrupting the work of thread #1
lock
keyword requires a token to work- Can use anything as a token
- Often you simply declare an
object
for this - If working within a private method of an instance it's generally easiest to lean on the
this
keyword/reference - Purpose of all this is to define the scope of the code to which you are applying
lock
- Syntax/example (public method using
object
as a token):
public class MyClass
{
// Create a lock token…
private object threadLock = new object();
public void SomeMethod()
{
// Code here is not thread safe…
// Use the lock token…
lock(threadlock)
{
// Code within this scope IS thread safe…
}
}
} - Syntax/example (private method using
this
as a token):
public class MyClass
{
private void SomeMethod()
{
// Use current obj as token…
lock(this)
{
// Code within this scope IS thread safe…
}
}
}
- Synchronization using the
System.Threading.Monitor
type- "The C#
lock
statement is really just shorthand notation for working with theSystem.Threading.Monitor
class." (Troelsen & Japikse: 7th Ed., p. 722) - To use the
Monitor
class- You still need a "threadlock" - or, if working with a private instance level method you can still lean on
this
- Wrap your operative code inside a
try
block - Wrap that
try
block inside aMonitor.Enter(yourThreadLockObject)
block - Finish with a
finally { Monitor.Exit(yourThreadLockObject); }
statement
- You still need a "threadlock" - or, if working with a private instance level method you can still lean on
- In fact, under simple scenarios using the
Monitor
class will generate the exact same CIL code as using thelock
keyword - Only reasons to use the
Monitor
class vs. the simplerlock
keyword:Monitor
provides some functionality not available with thelock
keyword- Examples (i.e.,
Monitor
methods):Monitor.Wait()
Monitor.Pulse()
Monitor.PulseAll()
- "The C#
- Synchronization using the
System.Threading.Interlocked
type- Rather surprisingly, value assignments & simple math operations are not atomic
- Use an i
Interlocked
type to lock down a single data point - Note: this involves less system overhead than using
Monitor
- Static members
CompareExchange()
- Compares 2 values to see if they are the same
- If so swaps one of the values for a 3rd
Increment()
&Decrement()
- changes value by 1Exchange()
- swaps 2 values
- These kinds of operations are in fact pretty common in multi-threaded environment
- Syntax/example - using the
lock
keyword:
public void IncrByOne()
{
lock(someToken)
{
someIntVal++;
}
} - Syntax/example - same, using the
Interlocked
class:
public void IncrByOne()
{
// Get a new value in the process…
int newVal = Interlocked.Increment(ref someIntVal);
}
- Synchronization using the
[Synchronization]
attribute- Member of
System.Runtime.Remoting.Contexts
namespace - When used on a class locks down all members of the class as thread-safe
- Wrinkle:
- This attribute means that the decorated object is placed within a synchronized context
- However, objects that need to stay inside a contextual boundary should derive from
ContextBoundObject
- Syntax/example:
using System.Runtime.Remoting.Contexts;
// Render all methods of class thread-safe…
[Synchronization]
public class MyClass : ContextBoundObject
{ // Methods… } - Downsides of using the
[Synchronization]
attribute- You are locking down all class members, regardless of whether those members are working on thread-sensitive data
- This can hit performance
- Member of
- General
- Programming with timer callbacks
- For use when you have to call some method at regular intervals
- You work with the
TimerCallback
delegate, whose pointed-to methods must:- Take a single
object
parameter (i.e.,state
) - Return
void
- Take a single
- Example:
class Program
{
static void Main(string[] args)
{
TimerCallback timeCB = new TimerCallback(PrintTime);
// Params: delegate, state (object), msecs to wait, msecs between calls.
Timer t = new Timer(timeCB, "Hello from Main", 0, 1000);
Console.WriteLine("Hit key to terminate…");
Console.ReadLine();
}
static void PrintTime(object state)
{
Console.WriteLine($"Time is: " +
$"{DateTime.Now.ToLongTimeString()}, Msg: {state.ToString()}");
}
}
- The CLR
ThreadPool
- CLR automatically maintains a pool of threads
- This pool of threads is accessed whenever you utilize
BeginInvoke()
on a delegate - This pool is represented by
ThreadPool
type (inSystem.Threading
namespace) - You can 'queue up' a method on that pool
- Invoke the static
QueueUserWorkItem()
method, which:- Returns a
bool
- Has 2 overloaded signatures:
- You must pass in a
WaitCallBack
delegate (typically calledcallBack
) - Can additionally pass in an
object
(forstate
)
- You must pass in a
- Returns a
- Notes:
- Pointed-to method must:
- Take a single
object
(typically calledstate
) as a parameter - Return nothing - i.e.,
void
- Take a single
- If you do not pass in a 'state'
object
to theQueueUserWorkItem()
method the CLR passes in anull
on your little behalf
- Pointed-to method must:
- Example:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Main thread started. " +
$"ThreadID = {Thread.CurrentThread.ManagedThreadId}");
Printer p = new Printer(); // Printer class definition not show.
WaitCallback workItem = new WaitCallback(PrintTheNumbers);
// Queue the method 5 times.
for(int i = 0; i < 5; i++)
{
ThreadPool.QueueUserWorkItem(workItem, p);
}
Console.ReadLine();
}
static void PrintTheNumbers(object state)
{
Printer task = (Printer)state;
task.PrintNumbers();
}
}
- Invoke the static
ThreadPool
advantages- Tends to be more efficient - minimizes number of threads spawned
- Somewhat simpler to use than threading primitives
- Stick to threading primitives when:
- You need foereground threads (pooled threads are always background threads)
- You need to set thread priority (pooled threads always set to
ThreadPriority.Normal
) - You need to identify/name a thread (in order to abort, discover, etc.)
- Programmatically creating secondary threads
- Parallel programming
- Parallel programming using the Task Parallel Library (aka, TPL)
- General
- Introduced with .NET 4.0
- Tends to simply things with little sacrifice in control
System.Threading.Tasks
namespace- Types in this class collectively referred to as Task Parallel Library
- As a whole, TPL will
- Automatically distribute tasks across available CPUs
- Use CLR thread pool to do so
- Handle most low-level details (scheduling, state management, etc.)
- The
Parallel
class- Allows you to iterate over a collection of data in a parallel manner
- Note: the data collection must implement
IEnumerable<T>
Parallel.For() & Parallel.ForEach()
- The 2 primary methods of the class (both are static)
- Have numerous overloaded versions
- Handle the body of the loops in a parallel manager, utilizing CLR thread pool
- Also automatically handle concurrency
- Handle "just" as you would
for
&foreach
loops- However, body of a
Parallel
loop must consist solely of 1 of 2 delegates:System.Func<T>
(which points to methods with return values)System.Action<T>
(which points to methods with no return values)
- Typically simplify you code by replacing an explicit delegate declaration with either:
- Anonymous method
- Lambda expression
- However, body of a
- Data parallelism
- Thread affinity
- In terms of hardware, the term means that certain threads are assigned to certain processors
- In terms of UIs, the term means that UIs can only interact with the thread that created them, not with secondary threads
- Thread affinity obviously presents a hurdle when trying to employ mult-threading in an app with a GUI
- Data parallelism ultimately designed to allow you to have background threads working without blocking primary thread
- Example (from Troelsen & Japikse: 7th Ed., p. 731):
private void ProcessFiles()
{
// Look up all *.jpg files & make a new folder for modified data.
string[] files = Directory.GetFiles(
@"D:\Bill\Pictures", "*.jpg", SearchOption.AllDirectories);
string newDirectory = @"D:\Bill\Pictures\FlippedPix";
Directory.CreateDirectory(newDirectory);
// Anonymous Action<T> delegate construct.
Parallel.ForEach(files, currentFile =>
{
string fileName = Path.GetFileName(currentFile);
using (Bitmap bitmap = new Bitmap(currentFile))
{
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitmap.Save(Path.Combine(newDirectory, fileName));
}
});
}
- Thread affinity
- Accessing UI elements on secondary threads
- Debugging mult-threaded apps
- This can be tricky!
- Threads are not at all guarenteed to act in dev/debugging as they will in production
Invoke()
method- Tends to obviate the just-mentioned issue
- Member of the
Control
class, which is parent of Windows Forms - Takes a
System.Delegate
as a parameter - This method allows one to access UI controls in a thread-safe manner
- Typically, one authors the code using an anonymous delegate
- Example:
using (Bitmap bitmap = new Bitmap(currentFile))
{
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitmap.Save(Path.Combine(newDirectory, fileName));
// Print out the ID of the thread processing the current image.
this.Invoke((Action)delegate
{
this.Text = string.Format($"Processing {fileName} " +
$"on thread {Thread.CurrentThread.ManagedThreadId}");
});
} - Notes:
- The above code will still block the primary thread - i.e., UIs will still be locked
- For WPF apps one utilizes
this.Dispatcher.Invoke()
- Debugging mult-threaded apps
- The
Task
classFactory
property ofTask
is of typeTaskFactory
TaskFactory.StartNew() / Task.Factory.StartNew()
method- Takes an
Action<T>
delegate as a parameter - The method pointed to by that delegate will be invoked in an asynchronous manner
- You typically replace the
Action<T>
delegate with a lambda expression - Example:
private void btnProcessImages_Click(object sender, EventArgs e)
{
// Start a new "task" to process the files.
Task.Factory.StartNew(() =>
{
ProcessFiles();
});
}
- Takes an
- Handling cancelation requests
- Handled via cancelation tokens
- To wire up
- Create a
CancellationTokenSource
object- Declare it as
private
within your Windows Form - Example:
public partial class MainForm : Form
{
private CancellationTokenSource cancelToken =
new CancellationTokenSource();
// …
}
- Declare it as
- Define a
ParallelOptions
object- Do so inside the method which you might want to cancel
- Attach the
Token
property of your cancel token to theParallelOptions
object - Set any other desired properties of the
ParallelOptions
object - Example:
private void ProcessFiles()
{
// Define ParallelOptions object to hold cxl token.
ParallelOptions parOpts = new ParallelOptions();
parOpts.CancellationToken = cancelToken.Token;
parOpts.MaxDegreeOfParallelism =
System.Environment.ProcessorCount;
//…
}
- Add a
try/catch
block- Wrap the
Parallel.ForEach()/Parallel.For()
portion of your code inside atry
block - Set the ensuing
catch
block to handle aOperationCanceledException
instance - Inside the
catch
block utilizethis.Invoke(Action<T>)
- Example:
catch (OperationCanceledException ex)
{
this.Invoke((Action)delegate
{
this.Text = ex.Message; // For example.
});
}
- Wrap the
- Modify the
Parallel.ForEach()/Parallel.For()
portion of your code- Pass in/add your
ParallelOptions
object as a parameter - Add a
ThrowIfCancellationRequested()
line at the top of the loop - Example:
try
{
// Process the images in a parallel manner.
Parallel.ForEach(files,parOpts,currentFile =>
{
parOpts.CancellationToken.ThrowIfCancellationRequested();
// Implementation code…
});
}
- Pass in/add your
- Create a
- Task parallelism using the
Parallel
class-
Parallel
class is useful beyond data parallelism (i.e.,Parallel.ForEach() & Parallel.For()
- Specifically, can use
Parallel.Invoke()
for asynchronous operations- Typically simpler to use than employing delegates or
System.Threading
members - For its parameter
Parallel.Invoke()
takes an array ofAction<>
delegates - Note: as always, the easiest way to supply
Action<>
delegates is to do so via lambda expressions - Syntax/example (from Troelsen & Japikse: 7th Ed., pp. 735‑8):
private void btnGetStats_Click(object sender, EventArgs e)
{
// theEBook is text of a downloaded book.
string[] words = theEBook.Split(new char[] {' ', etc.});
string[] tenMostCommon = null;
string longestWord = string.Empty;
// Call 2 private methods (not shown).
Parallel.Invoke(
() =>
{
tenMostCommon = FindTenMostCommon(words);
},
() =>
{
longestWord = FindLongestWord(words);
});
// Code to handle/show results…
}
- Typically simpler to use than employing delegates or
-
- General
- Parallel LINQ queries (PLINQ)
- General
- PLINQ queries are simply LINQ queries designed to run in parallel
- PLINQ is a set of extension methods
- As with
Parllel
class, PLINQ can ignore the instuctions to process code in parallel- Running code in parallel can be "expensive"
- PLINQ will run code synchronously if it determines that would be faster
ParallelEnumerable
class methods:AsParallel()
WithCancellation()
- Presumes that you have provided a cancellation token
- Method will then periodically monitor the token to handle any cancelation request
WithDegreeOfParallelism()
- specifies max number of processors to be usedForAll()
- Processes results in parallel without first merging back to calling thread
- This is different from how results are handled when using
foreach
with LINQ
- Opting into a PLINQ query
- As seen in the example to follow, you simply invoke the
AsParallel()
method- You do this inside the LINQ/PLINQ query
- You invoke the method on variable representing the source data you are querying
- The TPL will then look to shift the workload to an available CPU
- As seen in the example to follow, you simply invoke the
- Canceling a PLINQ query
- As shown previously, to handle cancelations define a private
CancellationTokenSource
(notCancellationToken
!!!) - To the
AsParallel()
method appendWithCancellation(myCxlToken.Token)
- As shown previously, to handle cancelations define a private
- Syntax/example (from Troelsen & Japikse: 7th Ed., pp. 738#8209;40)
public partial class MainForm : Form
{
private CancellationTokenSource cxlToken =
new CancellationTokenSource();
// Initializing MainForm code omitted.
private void ProcessIntData()
{
// Get a big array of integers.
int[] source = Enumerable.Range(1, 10000000).ToArray();
try
{
// Find ints divisible by 3, in descending order.
int[] modThreeIsZero =
(from num in source.AsParallel().WithCancellation(cxlToken.Token)
where num % 3 == 0
orderby num descending
select num).ToArray();
MessageBox.Show($"{modThreeIsZero.Count()} such integers");
}
catch(OperationCanceledException ex)
{
this.Invoke((Action)delegate
{
this.Text = ex.Message;
});
}
}
private void btnExecute_Click(object sender, EventArgs e)
{
// Start a new Task to process the ints.
Task.Factory.StartNew(() =>
{
ProcessIntData();
});
}
private void btnCancel_Click(object sender, EventArgs e)
{
cxlToken.Cancel();
}
}
- General
- Asynchronous calls with the
async
keyword- General
- The C#
async
&await
keywords - Naming conventions for async methods
- Async methods returning
void
- Async methods with multiple
awaits
- Retrofitting code with
async/await
- Parallel programming using the Task Parallel Library (aka, TPL)
- The Process/AppDomain/Context/Thread relationship
- File I/O & Object Serialization
- Multithreaded & Parallel Programming
- Windows Presention Foundation
- WPF & XAML
- Programming with WPF Controls
- WPF Graphics Rendering Services
- WPF Resources, Animations, Styles, & Templates
- Notifications, Commands, Validation, & MVVM
- The Model-View-ViewModel pattern
- Model
- "The Model is the object representation of your data." (Troelsen & Japikse: 7th Ed., p. 1130)
- The Model is not, say, the database data - again, it is, essentially, the corresponding POCOs
- The Model typically&helip;
- Includes validation
- Are configured as observables
- View
- This is the app UI
- Should have minimal code
- All business rules & data persistence should live elsewhere
- ViewModel
- Serves as the mechanism for the View to get access to data
- ViewModel also effectively controls the View
- User "instructions" are conveyed from the View to the ViewModel via commands
- Code in VM then instructs the View re what to do
- Anemic Models vs.anemic ViewModels
- Relates to where to host items like validation & observable patterns
- Anemic Model proponents: hosting anything other than strict data representation in Models violates separation of concerns
- Anemeic ViewModel proponents: hosting those items within the Model classes reduces code duplication
- Troelsen & Japikse seem to lean towards anemic ViewModel camp (7th Ed., p. 1131)
- Model
- WPF Binding Notication System
- General
- Winforms
- Doesn't have a notification system
- Ergo, your code-behind has to invoke
Refresh
on controls as appropriate - Typcially devs bullet proof Winforms apps by invoking
Refresh
all over their code - This, of course, leads to superfluous code & method invocations
- XAML-based apps
- You wired your data objects up to notification system(s)
- Such objects are referred to as observables
- Observables raise events when a ppty or collection changes
- Can even pick & choose which properties should (and should not) raise these change events
- XAML has a binding systems that listens for these events
- Fabulous system, though it does entail authoring a lot of code
- Winforms
- Observable models & collections
- See mark-up, Troelsen & Japikse: 7th Ed., pp. 1332‑3
- Adding bindings & data
- When a you bind data to an element without a
- You have to provide/declare a
DataContext
to bind a XAML element to data - Until/unless overridden, that declaration holds for all child elements
- Another way of looking at this is that XAML elements work up its element tree to find its
DataContext
- Example&ensp(from Troelsen & Japikse: 7th Ed., pp. 1332‑4):
- Master/detail grid
- 1st row of grid has a combo box, where the user selects the ID of a car in inventory
- Next few grid row contain labels (column 0) & then details about the selected car (column 1)
- Syntax/example:
<!-- Data context declaration for entire Grid -->
<Grid Grid.Row="1" DataContext="{Binding ElementName=cboCars, Path=SelectedItem}">
<!-- Grid.ColumnDefinitions & Grid.RowDefinitions… -->
<Label Grid.Column="0" Grid.Row="0" Content="Make"/> <!-- etc. -->
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Path=Make}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Path=Color}"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Path=PetName}"/>
- Programmatically changing data
- Syntax/example (from Troelsen & Japikse: 7th Ed., pp. 1335):
private void BtnAddCar_Click(object sender, RoutedEventArgs e)
{
var maxCount = _cars?.Max(x => x.CarID) ?? 0;
_cars?.Add(new Inventory()
{
CarID = ++maxCount,
Color = "Yellow",
Make = "VW",
PetName = "Birdie"
});
}
private void BtnChangeColor_Click(object sender, RoutedEventArgs e)
{
var car = _cars.FirstOrDefault(x =>
x.CarID == ((Inventory)cboCars.SelectedItem)?.CarID);
if (car != null)
{
car.Color = "Pink";
}
} - At this point note (unfortunately):
- After clicking the Change Color button you will not see the change within your form unless you change to another verhicle & then come back
- After clicking the Add Car button the combo box will still only show the original vehicles
- Syntax/example (from Troelsen & Japikse: 7th Ed., pp. 1335):
- Observable models
- Observable collections
- General
- Validation
- General
- Updating code for validation
- The validation class
- Validation options
- Data annotations
- General
- Adding data annotations
- Checking for data annotation-based validation errors
- Customizing the ErrorTemplate
- Creating custom commands
- General
- Implementing the
ICommand
interface - Updating
MainWindow.xaml.cs
- Updating
MainWindow.xaml.cs
- Attaching
Command
to theCommandManager
- Testing an application
- Adding further commands
- Fully implementing MVVM
- General
- Moving the data source out of the View
- Moving the
Commands
to the View Model
- Updating DALs for MVVM
- Full MVVM
- General
- Using
ObjectMaterialized
with Entity Framework
- The Model-View-ViewModel pattern
- Bibliography
- Troelsen, Andrew & Japikse, Philip. C# 6.0 and the .NET 4.6 Framework. 7th Ed. United States of America: Apress, 2015. Print.
- Troelsen, Andrew. Pro C# 2010 and the .NET 4 Platform. 5th Ed. United States of America: Apress, 2010. Print.
- Troelsen, Andrew. Pro C# 2008 and the .NET 3.5 Platform. 4th Ed. United States of America: Apress, 2007. Print.
- MSDN Library
- Charlie Calvert's Community Blog (MSDN Library)
- Wikipedia
- General
- Windows Forms
- General
System.Windows.Forms.dll
- primary assemblySystem.Drawing.dll
- For GDI+ API
- Provides for 2-D drawing
Systems.Windows.Forms.DataVisualization.dll
- New with .NET 4.0
- Provides
- Charting
- 3D rendering
- Hit-testing support
- Main namespace:
Charting
- Windows Forms Namespaces
- Main namespace:
System.Windows.Forms
- Categories of contained types
- Core Infrastructure (examples)
Form
Application
- Types which enable interoperability with:
- ActiveX controls
- WPF custom controls
- Controls
- All derive from
Control
base class - Examples
Button
MenuStrip
ProgressBar
DataGridView
- All derive from
- Components
- Types which:
- Do not derive from the
Control
class - Still provide visuals to a Windows Form app
- Do not derive from the
- "Many… are not visible at runtime, but can be configured visually at design time." (Troelsen: 5th Ed., p. 1512)
- Examples
ToolTip
System.Component.BackgroundWorker
- Types which:
- Common Dialog Boxes
- Examples
OpenFileDialog
PrintDialog
ColorDialog
- Note: can construct custom dialog boxes
- Examples
- Core Infrastructure (examples)
- There are > 100 types within
System.Windows.Forms
- Main namespace:
- Building a Windows Forms App
- General
- At a minimum all WinForms apps need:
- A class which inherits from the
Form
class (typically calledMainWindow
) - Some method which calls
Application.Run()
method
- A class which inherits from the
- Using VS
- VS will automatically open the GUI Forms designer if you:
- Double-click on a
*.cs
file from where the*.cs
file contains a class inheriting from theForm
type - That
Form
-derived class is the first class in the*.cs
file
- Double-click on a
- If you do not want the visual designer right-click on the
*.cs
file
- VS will automatically open the GUI Forms designer if you:
- By default all Forms have buttons
- Launching WinForms from a console app
- By default the
/target
flag for the C# compiler is set to/target:exe
- This causes a console window to appear behind your Windows Form(s)
- To change this behavior
- Change the selection to
- By default the
- Can build custom constructors for your
Form
-derived windows where you pass in parameters for size, title, etc.
- At a minimum all WinForms apps need:
- Populating the
Controls
Collection- Property of the
System.Windows.Forms.Control
base class - Of type
ControlsCollection
- Members of
ControlsCollection
typeAdd()
&AddRange()
- Take
Control
-derived types as arguments - Can also pass in an
array
ofControl
-derived types
- Take
Clear()
Count
Remove() & RemoveAt()
- Typical steps for adding controls to a
Form
-derived type- Within the
Form
-derived type define a UI member variable - Set the look & feel of that element
- Invoke
Controls.Add()
to add that UI to the form'sControlsCollection
- Within the
- Syntax/example:
// This is the main window…
class MainWindow : Form
{
// Members for a simple menu system…
private MenuStrip mnuMainMenu = new MenuStrip();
private ToolStripMenuItem mnuFile =
new ToolStripMenuItem();
private ToolStripMenuItem mnuFileExit =
new ToolStripMenuItem();
// Ctors (include calls to BuildMenuSystem() - not shown)…
public MainWindow() { }
public MainWindow(string title, int height, int width)
{ // Set various properties… }
private void BuildMenuSystem()
{
// Add File menu to the main menu…
mnuFile.Text = "&File";
mnuMainMenu.Items.Add(mnuFile);
// Now add the Exit menu to the File menu…
mnuFileExit.Text = "E&xit";
mnuFile.DropDownItems.Add(mnuFileExit);
mnuFileExit.Click += (o, s) => Close();
// Set the menu for this form…
Controls.Add(mnuMainMenu);
MainMenuStrip = this.mnuMainMenu;
}
} - Notes:
MenuStrip
type == whole menu systemToolStripMenuItem
type can be either- Top-most menu item
- Any submenu item
- The
MainMenuStrip = this.mnuMainMenu;
line- Can seem redundant or superfluous
- However, allows you to build multiple menu systems & then attach whichever one you want at runtime!
Click
event works with standardSystem.EventHandler
delegate- Could use
Application.Exit()
instead ofClose()
, butApplication.Exit()
won't fire any of the close events
- Property of the
System.Event.Args
&System.EventHandler
- Parameters for
System.EventHandler
delegate:System.Object
- reference to the object firing the eventSystem.Event.Args
- Role of the
System.Event.Args
parameter- Often hard to find any use for it
- Only extends
System.Object
- Provides very little additional functionality
- Definition:
public class EventArgs
{
public static readonly EventArgs Empty;
static EventArgs();
public EventArgs();
}
- Only extends
- However, it does serve as a the base class for other, more useful types
MouseEventArgs
- provides details about current mouse statusKeyEventArgs
- gives info about which key user pressedPaintEventArgs
- Often hard to find any use for it
- Parameters for
- General
- VS Windows Forms Project Template
- Visual Designer Surface
- You will spend a lot of time working with
- Typically you change the name of
Form1.cs
toMainWindow.cs
- Dissecting the Initial Form
- In
- Notes:
- The
MainWindow
class is defined aspartial
InitializeComponent()
is defined in the 'other'partial
class file- The name of this, second,
partial
class file always ends withDesigner.cs
- Changes you make in the
InitializeComponent()
method window are automatically reflected in the code within the
- The
- The
*.Designer.cs
files- There is one for every form you create
- Mucking around in this auto-generated code will lead to tears (i.e., do your work in the window)
- Dissecting the
Program
Class- As always, provides the app's entry point - i.e.,
Main()
Main()
method- Fires up
Application.Run()
- Carries
[STAThread]
attribute- Handles the possibility that the runtime must create any COM objects (e.g., ActiveX controls)
- If COM objects are created they are placed in the single-threaded apartment
- COM-maintained area
- Ensures thread safety, even if/where COM object was not created in a thread-safe manner
- Fires up
- As always, provides the app's entry point - i.e.,
- Visually Building a Menu System
- Of course, can drag & drop controls off the
- As soon as you do so you typically are in a control editor area
- Additionally, the editor very often will have a small triangle in upper R corner
- Clicking on the triangle will open an über-handy inline editor
- To set up event handlers
- Click on your control of interest
- , then select event of interest
- Can then either
- Name the event handler
- Double-click to let VS stub out & name the event handler for you
- Note: VS also automatically attaches the event-handler within the
*.Designer.cs
file
- Visual Designer Surface
- Anatomy of a
Form
- General
Form
is the type of any window in an app- Main & child windows
- Modal & modeless dialog boxes
- Inheritance Chain
System.Object
System.MarshalByRefObject
- "Types deriving from this class are accessed remotely through a reference to (not a local copy of) the remote type." (Troelsen: 5th Ed., p. 1525) [emphasis in original]System.ComponentModel.Component
- Provides default implementation of
IComponent
interface - Component (in .NET) is something which may not be visible at runtime but which supports design-time editing
- Provides default implementation of
System.Windows.Forms.Control
- "defines common UI members for all Windows Forms UI controls, including theForm
type itself" (Troelsen: 5th Ed., p. 1526)System.Windows.Forms.ScrollableControl
- obvSystem.Windows.Forms.ContainerControl
- Pertains to controls that can serve as containers for other controls
- Provides focus-management functionality
System.Windows.Forms.Form
- again, base class for any- Form
- Multiple Document Interface (MDI) child
- Dialog box
- All of these base classes, as one might expect, provide a lot of methods, properties, & events
Control
Class Functionality- Of all the base classes just listed, this is 1 of the 2 on which a WinForms developer really must have a good handle
- Main properties, by type
- Basic UI of the control
Backcolor, Forecolor
BackgroundImage
Font
Cursor
- Positioning of control within its container
Anchor
Dock
AutoSize
- Control dimensions
Top, Left, Bottom, Right
Bounds
ClientRectangle
Height, Width
- State of the control (
bools
)Enabled
Focused
Visible
ModifierKeys
- Static
- Current state of the modifier keys (e.g., Shift, Alt, & Ctrl)
- Of type
Keys
MouseButtons
- Static
- Of type
MouseButtons
- Tab order of a control
TabIndex
TabStop
Opacity
- 0.0 is completely transparent
- 1.0 is completely opaque
Text
Controls
- Generally of type
ControlsCollection
- Gives you child controls
- Generally of type
- Basic UI of the control
- Main events, by type
- Mouse interaction
Click, DoubleClick
MouseEnter, MouseLeave
MouseUp, MouseDown
MouseMouse
MouseHover
MouseWheel
- Keyboard interaction
KeyPress
KeyUp, KeyDown
- Dray-and-drop interaction
DragDrop
DragEnter
DragLeave
DragOver
Paint
- for interaction with GDI+ rendering
- Mouse interaction
- Methods
OnXXX
methods- Methods such as
OnMouseMove
OnKeyUp
OnPaint
- All are…
- Virtual methods
- Default event handlers for corresponding events
- Overriding the methods gives you the "ability to perform any necessary pre- or post-processing of the event before (or after) invoking the parent's default implementation" (Troelsen: 5th Ed., p. 1528)
- Generally use these methods if you create a custom control…
- Which derives from a standard control
- Where you want to inherit event-handling
- VS default: creation of a
XXX_EventName
handler
- Methods such as
- Other methods
Show(), Hide()
- Shows or hides control (obv)
- Sets control's
Visible
(boolean
) property
Invalidate()
- Sends a
Paint
event - Note: this causes a control to redraw itself
- Sends a
Form
Class FunctionalityForm
class is generally the direct base class for any forms you createForm
, of course, adds properties, methods, & events beyondControl
class- Especially true with
- Main windows
- MDI child windows
- Dialog boxes
- Properties
AcceptButton, CancelButton
- set which button is 'clicked' when user hits Enter or EscActiveMdiChild, IsMdiChild, IsMdiContainer
- for MDI appsControlBox
- sets whether form has max, min, & close buttonsFormBorderStyle
- takes aFormBorderStyle
enumMenu
- for docking a menu to the formMaximizeBox, MinimizeBox
- sets whether these controls are enabledShowInTaskBar
StartPosition
- takes aFormStartPosition
enumWindowState
- Controls how the form appears on startup
- Takes a
FormWindowState
enum
- Methods
Activate()
Close()
CenterToScreen()
LayoutMdi()
- Use to arrange each child form within its parent form
- Takes a
MdiLayout
enum
Show()
- display the form as a modeless windowShowDialog()
- display the form as a modal dialog box
- Events
Activated, Deactivated
- fired when the form is given/loses focus on the desktopFormClosed, FormClosing
Load
- fired…- When the form has been allocated into memory
- Before the form is visible on screen
MdiChildActivate
- fired when any child window is activated
- Life Cycle of a
Form
Type- As with any GUI API, a
Form
triggers many events in the course of its lifetime - Specifics
- Life cycle begins with invocation of a constructor
- Form then passed into
Application.Run()
Load
event- Fires once the
Form
has been allocated onto the managed heap - Use the event handler to do things like:
- Set look & feel of
Form
- Set up any contained child controls (e.g.,
ListBoxes, TreeViews
) - Allocate resources, such as db connections or proxies to remote objects
- Set look & feel of
- Fires once the
Activate (& Deactivate)
eventsFormClosing & FormClosed
- Use
FormClosing
for "Are you sure?" prompts FormClosingEventHandler
delegate & theFormClosingEventArgs
parameter- Set
Cancel
totrue
to cancel theClose()
call - Set
Cancel
tofalse
to allow theFormClosed
event to fire
- Set
- Use
- Syntax/example:
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
// Handle various lifetime events…
Load += MainWindow_Load;
Activated += new EventHandler(MainWindow_Activated);
FormClosing += MainWindow_FormClosing;
}
void MainWindow_Load(object sender, EventArgs e)
{ // Event handling code… }
void MainWindow_Activated(object sender, System.EventArgs e)
{ // Event handling code… }
// Note 2nd parameter…
void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
{
// Show a message box with Yes and No buttons…
DialogResult dr = MessageBox.Show("Do you REALLY want to close this app?",
"Closing Event!", MessageBoxButtons.YesNo);
if (dr == System.Windows.Forms.DialogResult.No)
e.Cancel = true;
else
e.Cancel = false;
}
}
- As with any GUI API, a
- General
- Responding to Mouse & Keyboard Activity
- General
- Typically Mouse event handlers work the
System.Windows.Forms.MouseEventHandler
delegate - That delegate expects a
MouseEventArgs
for its 2nd parameter MouseEventArgs
propertiesButton
- Returns a
MouseButtons
enumeration - Tells you which button was pressed
- Returns a
Clicks
- Gets an
int
- Number of times the mouse button was pressed & released
- Gets an
Delta
- Gets a signed
int
- Counts number of detents (single notch of the mouse wheel) for the current wheel rotation
- Gets a signed
Location
- aPoint
representing the X, Y location of the mouse
- Typically Mouse event handlers work the
- Determining Which Mouse Button Was Clicked
- Again, you will be working with a
MouseButtons
enum XButton1, XButton2
are the forward & back navigation buttons on many a mouse
- Again, you will be working with a
- Determining Which Key Was Pressed
- Respond to
KeyUp & KeyDown
events - These events work with the
KeyEventHandler
delegate KeyEventArgs
propertiesAlt, Control, Shift
-bools
indicating whether these keys have been pressedHandled
- abool
indicating whether the event was fully handled in your handlerKeyCode
- Returns a
Keys
type - Code for either
KeyDown
orKeyUp
event
- Returns a
Modifiers
- Returns a
Keys
type - Tells you which modifier key - i.e., Ctrl, Shift, and/or Alt - was pressed
- Returns a
- Respond to
- General
- Designing Dialog Boxes
- General
- There is no
Dialog
base class in Windows Forms (many other GUI APIs do have that kind of a base class) - Instead, you simply derive from the
Form
class - Sizing
- You very often do not want your dialog boxes resized
- When that is the case
- Set
FormBordersStyle
toFormBordersStyle.FixedDialog
- Set
MinimizeBox, MaximizeBox
properties tofalse
- Set
- Typically, you also set
ShowInTaskBar
tofalse
StartPosition
toCenterParent
- There is no
- The
DialogResult
Property- This property is available on
Button
objects - Can set to
OK, Cancel, Yes, No,
others
- This property is available on
- Configuring the Tab Order
TabStop
propertybool
- Sets whether the user can 'tab to' this control
TabIndex
- Sets the order in which a user tabs through controls
- Zero-based
- Tab Order Wizard
- Access via
- Note: only available when is active
- Once active can then click through in desired order
- Setting the Form's Default Input Button
- Default input button == the button which the user effectively clicks by pressing Enter
- Syntax/example:
public partial class MyDialogForm : Form
{
public MyDialogForm()
{
InitializeComponent();
// Set default input button…
this.AcceptButton = btnOK;
}
} - Note: if you need to simulate the user pressing the Esc key set the
CancelButton
property on your form
- Displaying Dialog Boxes
Show()
- Modeless form
- Ergo, allows user to switch back & forth between dialog form & main window
- Returns
void
ShowDialog()
- Displays your form in modal form
- Method returns a
DialogResult
enum - Therefore, you invoke
ShowDialog()
& handle user button-clicks on the dialog 'all at once' - Syntax/example:
// Handle some click event to generate your dialog box…
private void myDialogBoxToolStripMenuItem_Click(
object sender, EventArgs e)
{
// Instantiate your form-derived type in memory…
MyDialogForm dlg = new MyDialogForm();
// Show as modal dialog box, & determine which button
// was clicked using the DialogResult return value…
if (dlg.ShowDialog() == DialogResult.OK)
{ // User clicked OK, so do something… }
}
- Alternate invocation
- Instead of invoking
Show()
orShowDialog()
on instance of the child form you can invoke either method on an instance of the parent form (e.g.,this.Show()
) - This approach
- Sets a z-ordering of the forms
- Insures that when you destroy the parent form that all children (of course, this only arises with modeless forms) are also destroyed
- Instead of invoking
- Visibility & controls
- Again, forms exist in memory as soon as they are allocated, but that allocation does not render them visible
- Visibility requires invoking
Show()
orShowDialog()
- Once
ShowDialog()
"returns" - i.e., the user clicked a button &ShowDialog()
returns aDialogResult
enum - the form still exists in memory- The form 'disappears'
- However, the form still exists in memory
- Ergo…
- Values in, say, a
TextBox
can be accessed - 'Out of the box' those controls are not directly accessible because they are private
- In theory you should create properties to access the values of form controls
- In practice most developers simply make the controls public
- To do so select a control then select
- Values in, say, a
- Form Inheritance
- A
form
is like any other (non-sealed) class - you can inherit from it! - This means that you can create a base form, & then create others with various enhancements to that base
- To do so:
- Pick the form from which you want to inherit
- Note:
- You must have compiled your project at least once to see available forms
- The button allows you to select from external assemblies
- Working with an inherited form in
window
- Each control has a small arrow in upper left to indicate inheritance
- Inherited controls you leave as is
- A
- General
- Rendering Graphical Data Using GDI+
- General
- Use for activities from creating charts to drawing
- All GDI+ technology is in
Systems.Drawing.dll
assembly - Reminder: GDI+ is used only in WinForms - i.e., not in WPF
- GDI+ namespaces
System.Drawing
- Main GDI+ namespace
- Handles basic rendering tools
- Fonts
- Pens
- Brushes (basic)
Graphics
type
System.Drawing.Drawing2D
- For more advanced 2D/vector graphics
- Examples:
- Gradient brushes
- Pen caps
- Geometric transformations
System.Drawing.Imaging
- For manipulating graphical images
- Examples:
- Changing a palette
- Obtaining image metadata
- Manipulating metafiles
System.Drawing.Printing
- all printing-relating typesSystem.Drawing.Text
- for manipulating font collections
System.Drawing
Namespace- Again, contains types for basic rendering
- Also contains utility types, such as
Color
Point
Rectangle
- Basic types
Bitmap
- For handling image data
*.bmp
& other types
- Brush objects (for filling the interiors of shapes)
Brush
Brushes
SolidBrush
- Others
BufferedGraphics
- "This type provides a graphics buffer for double buffering…" (Troelsen: 5th Ed., p. 1547)
- You use this when redrawing a display causes flickering
Color, SystemColors
- Define static, read-only properties
- Provide given colors for pens & brushes
Font, FontFamily
Graphics
- Very important type within the namespace
- Use to create a drawing surface
- Also provides methods for rendering text, images, & patterns
Icons, SystemIcons
- for both custom & standard iconsImage, ImageAnimator
Image
is the abstract base class forBitmap
Icon
Cursor
ImageAnimator
allows you to flip throughImage
-derived types at a set interval
Pen, Pens, SystemPens
- One uses pens to draw
Pens
type has several static properties that give you a 'colored'Pen
Point, PointF
- Both give you an (x,y) coordinate
Point
works with integer valuesPointF
works with float values
Rectangle, RectangleF
Size, SizeF
- height & widthStringFormat
- use to encapsulate string format specifications (e.g., alignment, line spacing)Region
- Gives you the interior of a geometric shape
- Works when that shape comprises rectangles & paths
- Role of the
Graphics
Type- Defines surface you will "draw" on
- Also defines scads of ancillary members
- Methods (partial list)
- For obtaining a
Graphics
object from a given image or from a GUI control (all static)FromHdc()
FromHwnd()
FromImage()
Clear()
- erases drawing surface & fills that surface with a specified color- For generating a given image or geometric pattern
- Note: all these methods require the use of
Pen
objects DrawArc()
DrawCurve()
DrawElipse()
DrawIcon()
DrawLine(), DrawLines()
DrawPath()
DrawRectangle(), DrawRectangles()
- Others
- Note: all these methods require the use of
- For filling the interior of a geometric shape
- Note: all these methods require the use of
Brush
objects FillEllipse()
FillPie()
FillPolygon()
FillRectangle()
FillPath()
- Others?
- Note: all these methods require the use of
- For obtaining a
- Obtaining a
Graphics
Object with thePaint
Event- One method which
Graphics
does not have is a public constructor - Ergo, you cannot
new
aGraphics
instance into existence - Typically you use the
Paint
event to obtain aGraphics
instance- The
Paint
event is an event of aForm
-derived type (i.e., one of your windows) - 2nd parameter for
PaintEventHandler
delegate is of typePaintEventArgs
PaintEventArgs
contains aGraphics
property/object
- The
- Syntax/example (taken directly from Troelsen: 5th Ed., pp. 1549‑50):
private void MainWindow_Paint(object sender, PaintEventArgs e)
{
// Get the graphics object for this Form…
Graphics g = e.Graphics;
// Draw a circle…
g.FillEllipse(Brushes.Blue, 10, 20, 150, 80);
// Draw a string in a custom font…
g.DrawString("Hello GDI+", new Font("Times New Roman", 30),
Brushes.Red, 200, 200);
// Draw a line with a custom (IDisposable) pen…
using (Pen p = new Pen(Color.YellowGreen,10))
{
g.DrawLine(p,80,4,200,200);
}
} - Raising the
Paint
event- Fires any time a window becomes dirty
- A window becomes soiled whenever it is
- Resized
- Partially or completely uncovered by another window
- Minimized & then restored
- Also appears to fire whenever the window is first created
- One method which
- Invalidating the Form's Client Area
- Do this when you don't want to wait for user to cause
Paint
event to fire via resizing form, etc. - Example:
- You have presented your user with a dialog box from which to pick an image
- Without your intercession the drawing area will not automatically show that image
- Syntax/example (taken from Troelsen: 5th Ed., p. 1151):
private partial class MainForm : Form
{
…
private void MainForm_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics
// Render selected image…
}
private void GetImageFromDialog()
{
// Show the dialog box & get new image, then repaint entire client area…
Invalidate();
}
}
Invalidate()
has been overloaded several times- Default behavior is to repaint entire area
- However, you can specify a rectangular area to repaint
- Syntax/example:
// Repaint upper left portion of a form…
private void RepaintUpperLeftArea()
{
Rectangle myRect = new Rectangle(0, 0, 25, 50);
Invalidate(myRect);
}
- Do this when you don't want to wait for user to cause
- General
- Building a Complete Windows Forms App
- See Troelsen: 5th Ed., pp. 1151‑9 for full example
- Main Menu System
- Tossing an ampersand into the name of a menu item flags the following letter for Alt-+key shortcuts
- For example, "Sa&ve" provides an Alt+S shortcut to the Save option
- Adding a single dash ("-") adds a separator in a menu
- Implementing the
ToolsMenu
Functionality- As discussed above, you can create your own, custom dialog boxes
- .NET comes with a number of built-in custom dialog boxes, such as
ColorDialog
- Bibliography
- Troelsen, Andrew. Pro C# 2010 and the .NET 4 Platform. 5th Ed. United States of America: Apress, 2010. Print.
- General
- VSTO 3.0
- Miscellaneous Subjects
- Computer Networking (in progress)
- General
- TCP/IP Five-Layer Network Model
- Layer Name | Protocol | Protocol data unit | Addressing
- Application | HTTP, SMTP, etc. | Messages | n/a
- Transport | TCP/UDP | Segment | Port #'s
- Network (aka internet layer) | IP | Datagram | IP address
- Data Link | Ethernet, Wi-Fi | Frames | MAC address
- Physical | 10 Base T, 802.11 | Bits | n/a
- Note - there is also a 7-layer model (see OSI model, below)
- Layer Name | Protocol | Protocol data unit | Addressing
- Basics of Networking Devices
- Cables
- Copper
- Most common
- Twisted pairs of wires
- 1s & 0s sent by changes in voltage
- Types
- Cat5
- Oldest
- Mostly replaced by Cat5e & Cat6
- Cat5e
- Cat6
- Cat5
- Crosstalk
- Means pulses on one wire getting picked up on another wire
- This obviously corrupts messages
- Upper level protocols can & will identify when this has occurred
- A message will be returned to the sender saying that the message needs to be resent
- Resending messages of course slows down overall transmission
- Cat5e does a better job than Cat5 of eliminating crosstalk
- Cat 6 is even better yet. However…
- More expensive
- More limited than Cat5e in physical distance of runs (at least for higher speed transmissions)
- Fiber
- Individual strands about the width of a human hair
- Uses pulses of light to transmit 1s and 0s
- Great for locations where there is a lot of electrical interference
- Pros
- Faster than copper
- Can make much longer cable runs
- Cons
- Expensive
- Fragile
- Copper
- Hubs & switches
- Cables only establish point-to-point connections
- Hubs
- Connect multiple computers
- A message sent by one computer is received by all on the hub
- Receiving computers must determine whether a message is one designed for it to receive
- This topography results in a lot of noise on the network
- Collision domain
- Segment of a network where only one device can communicate at a time
- This creates risk of data collisions - i.e., electrical pulses on a cable interfering with one another
- Hubs therefore require quiet periods in order for a device to send data
- Ergo, hubs are slow
- Hubs not very common these days
- Network switch
- AKA, a switching hub
- Inspects data sent out by a computer
- Routes the data to the 1 computer for which the message is intended
- Virtually eliminates collision domains
- This capability qualifies switches as data link devices (hubs are physical layer devices)
- With fewer data collisions (& the ensuing retransmissions) switches are faster than hubs
- Routers
- A device that knows how to send data between independent networks
- As such, a router is a network layer device
- Router reads entries in IP header of message it is handling in order to determine where to send the data packet
- Contain tables that guide where to send messages
- Home/small-office routers
- By far the most common kind of router
- Their routing tables are small
- Their main job is typically to forward messages to the user's ISP
- Core ISP routers
- Backbone of the internet
- These routers are where ISPs direct their outbound traffic
- Vastly more complicated & powerful than home routers
- Connected to large numbers of other core routers
- BGP
- This is how core routers communicate with each other
- Utilized to determine the optimal route by which to send a message
- Messages may have to traverse dozens of routers before reaching ultimate destination
- Servers & clients
- All points on the internet - computers, routers, etc. - are referred to as nodes
- Servers supply data; clients request data
- Servers don't have to be physical entities
- An application on a single computer can function as a server - even to a different application on that same machine
- Roles
- A server almost always functions as a client at various times
- That said, a node will almost always be primarily a server or a client
- Server vs. client "definition" determined by the primary purpose of a given node on a network
- Cables
- Physical Layer
- How bits are moved across wires
- Copper wires, once connected on a network, carry a constant electrical charge
- Modulation - the practice of varying the voltage of that charge
- Line coding
- The specifics of how modulation is managed in order to transmit 1s & 0s
- Entails the use of encoders & decoders along the wire
- Line coding currently capable of transmitting 10 billion bits/second
- Twisted pair cabling & duplexing
- Twisted pair is the most common type of network copper cable
- Each pair is a single channel
- Twisting the wires helps reduce
- Electrical interference
- Cross talk between pairs
- Inside the cable's outer jacket
- 8 wires, i.e., 4 pairs
- The number of pairs used depends on transmission technology in use
- Duplexing
- Means that there is 2-way communication across the cables
- Required for all modern networks
- How achieved
- 1 or 2 pairs are reserved for communicating in one direction
- A different 1 or 2 pairs handle communications in the reverse direction
- Full duplexing - Where back & forth communication across a cable is allowed to occur simultaneously
- Half duplexing
- Back & forth communication is allowed, but cannot occur simultaneously
- When a network experiences problems you sometime see messages that it has degraded to half duplex
- Simplex communication (fyi)
- Means data only flows 1 way
- A baby monitor is a simplex device/network
- Network ports & patch panels
- This is the area covering the physical ends of network cables
- RJ45 jacks
- RJ: "registered jack" (male end of a cable's termination/connection)
- Require RJ45 ports (i.e., the females)
- Other jack types exist, but RJ45s are by far the most common
- Network ports
- Any port which directly connects to a network device
- Again, typically an RJ45 port
- Switches, essentially by definition, have multiple network ports
- Servers & clients typically have but 1 or 2 network ports
- LED lights
- Network ports generally have 2
- Link light
- Yellow
- Indicates that cable is properly connected to 2 powered-on devices
- Activity light
- Green
- Flashes to indicate that data is being transmitted
- Switches
- Sometimes have only 1 LED light on their ports
- Can indicate other (i.e., trouble-shooting) information
- Interpreting tends to be device specific - i.e., RTFM
- Patch panels
- Nothing more than a block of multiple ports
- Allows for easy cable re-routing - à la early days of telephones when operators sat connecting calls by unplugging & plugging wires
- How bits are moved across wires
- Data Link Layer
- Ethernet & MAC addresses
- Ethernet
- The most common protocol for transmitting data between points on a network
- Primary purpose is to abstract away higher layers from having to be concerned with physical devices in use
- Fairly old technology
- Introduced in 1980
- 1st standards issued in 1983
- Developed when switches were not yet around
- Handling collision domains was a huge concern
- CSMA/CD
- Technique detects when communications channels are clear - i.e., that it is ok to transmit data
- A node waits to send data when the network is quiet
- If Device A's transmission is interrupted because Device B begins sending data then both transmissions stop
- The devices then wait a random interval of time before attempting to resend data
- MAC address
- Defined
- Globally unique identifier attached to an individual network device
- 48-bit number
- Typically 6 groupings of 2 hexadecimal numbers
- Octet any number that can be represented by 8 bits
- 2 hex digits can represent same number as an octet [16^2 = 2^8 = 256]
- Note: There are 16 ^ 12 possible MAC addresses (≈281 trillion)
- 2 components to MAC addresses
- OUI
- First 3 octets
- Assigned to individual hardware manufacturers by IEEE
- Note: this means you can always identify a device manufacturer from a device's MAC address
- Final 3 octets
- Assigned by manufacturer
- Requirement is that manufacturer assign its portion of a MAC address only once
- OUI
- Ethernet uses MAC address so that it can identify
- Message sending device
- Intended recipient
- A MAC address is how a device on a Collision domain determines which messages are intended for itself
- Defined
- Ethernet
- Unicast, multicast, & broadcast
- Unicast - a message intended for only one other device
- How ethernet identifies messages
- Identified within ethernet frame
- Unicast: least significant bit of first octet of destination MAC address is set to 0
- Multicast: that same bit is set to 1
- How machines on a collision domain handle received messages
- Unicast - easy for a device to determine whether the message is intended for itself or not
- Multicast - network devices can be configured to use MAC addresses to identify which multicast messages to accept (&, ergo, which to ignore)
- Broadcast
- A message intended for every device on a LAN
- Identified through the use of a broadcast address
- This address = FF:FF:FF:FF:FF:FF
- Ethernet frame
- Data packet - any single set of binary data sent across a network connection
- This doesn't depend on any protocol, network layer, whatever
- Simply a concept about data being sent from Point A to Point B
- Ethernet frame
- Data packets at the ethernet level
- A highly structured set of info sent in a specific order
- Allows devices at physical level to interpret the received bits & bytes intelligently
- Components - general
- All are mandatory
- Most have a fixed size
- Components (in order)
- Preamble - 8 bytes (i.e., 64 bits)
- Can be split into 2 sections
- 1st seven bytes
- Alternating 1s & 0s
- Partially acts as a buffer between frames
- Can also indicate time
- This allows network interfaces to synchronize themselves
- In turn allows interfaces to regulate speed at which they send data
- Last byte
- SFD
- Indicates the end of the preamble
- Destination MAC address - 6 bytes
- Source MAC address - 6 bytes
- Tag - 4 bytes
- Ethernet type - 2 bytes
- Describes the protocol of the contents of the frame
- Deep dive on these protocols below
- VLAN
- A technique whereby you have multiple logical LANs on same physical equipment
- VLAN frames can only be sent from switch interface specifically configured to generate VLAN frames
- Usually employed to segregate different forms of traffic
- Example:
- IP phone on one VLAN
- Regular data on another VLAN
- When utilized:
- Tag is replaced with a VLAN Tag (aka VLAN Header)
- Indicates the entire frame is a VLAN frame
- Payload - 46 to 1,500 bytes
- Obv contains raw info being transformed
- Also contains data from higher layers:
- Application
- Transport
- Network (i.e., IP)
- FCS
- Contains a checksum value for the entire frame
- Value is calculated via CRC against the frame
- CRCs
- Used across computing, not just with network traffic
- Ensure data integrity
- It's a mathematical computation
- Polynomial division to represent a larger set of data
- Computed by sending device, with result entered in FCS
- Receiving device recomputes - if it comes up with a different CRC number:
- The frame is discarded
- It is then up to higher level protocols to determine whether the data/frame needs to be retransmitted
- Preamble - 8 bytes (i.e., 64 bits)
- Data packet - any single set of binary data sent across a network connection
- Ethernet & MAC addresses
- TCP/IP Five-Layer Network Model
- Network Layer
- General
- Network layer is what allows communication across:
- Different networks
- The world
- MAC addresses
- Again, this is what allows devices to identify themselves with a single network
- A network switch can quickly
- Identify all MAC address that can be reached via each of its ports
- Then route traffic appropriately
- Relying on MAC address does not scale well, though
- MAC addresses are unordered
- Devices, of course, can & do
- Move frequently
- Attach to different networks
- Network layer is what allows communication across:
- IP Addresses
- Ipv4
- 32-bit number, broken down into 4 period-delimited octets
- Each octet is translated into its decimal equivalent
- These decimal equivalents can range from 0 to 255 (2 ^ 8 = 256)
- Result written in dotted decimal notation
- 'Break-down' of IP addresses
- Distributed to large organizations & companies
- Not controlled by hardware vendors
- Much more hierarchical than MAC addresses
- Ergo, easier to store data about
- Example:
- IBM 'owns' an IP address that begins with a 9
- When a sending router finds a 9.0.0.1 IP address (for example) all that router needs to know/determine is how to find the router which handles the IBM network
- IP address ownership
- Networks, not devices, own IP addresses
- Laptop example:
- Will always have the same MAC address
- IP address, however, changes depending on connecting at home, work, Starbucks, etc.
- Dynamic IP address
- Assigned via DHCP
- Assigned whenever you connect to a network
- Typically used for client devices
- Static IP address
- Assigned manually
- Typically reserved for
- Servers
- Network devices
- Ipv4
- IP Datagrams & Encapsulation
- IP datagram
- The name for a data packet at the network layer
- Data link layer wraps the bits & bytes it transmits inside an ethernet frame
- IP datagram header
- 32 bits
- A good bit more complex than the header of an ethernet frame
- Fields (in bit order)
- Version - 4 bits (i.e., bits 1 - 4)
- Most common type is still IPv4
- IPv6 is supposed to rule the world… someday
- Header Length - 4 bits
- With IPv4 protocol total length of header is almost always 20 bytes
- In fact, 20 bytes is the minimum length of an IP header
- Service Type - 8 bits
- Contains information about QOS technologies
- Primarily, this allows routers to make decisions about which IP datagrams are more important than others
- Total Length - 16 bits
- Identification - 16 bits
- Used to group messages together
- Because Total Length field is 16 bits, the largest number it can hold is 65,536
- Ergo, any IP datagram that would be larger than this must be split up into packets
- Every packet with the same ID Field are part of the same transmission
- Flag (4 bits) & Fragmentation Offset (12 bits) fields
- Not all networks can handle the same size datagram
- Fragmentation
- Process whereby a network breaks a single datagram into smaller datagrams
- Typically done when a network receives a datagram larger than the network can handle
- Flag field indicates datagram either:
- Is allowed to be fragmented
- Has already been fragmented
- Fragmentation Offset field is used to reassemble fragmented datagrams
- TTL - 8 bits
- Maximum number of router hops allowed before datagram is thrown away
- Value gets decremented by 1 every time it gets passed to a new router
- When value = 0 the router can toss the datagram
- Protocol - 8 bits
- Identifies the transport layer being used
- TCP & UDP are the most common
- Header Checksum - 16 bits
- Recall that TTL field is decremented at every router
- Ergo, the Header Checksum value must also be recomputed/verified at every router
- Source IP Address - 32 bits
- Destination IP Address - 32 bits
- Options - variable length
- Nothing is required here
- Primarily used for testing
- Padding
- Created because Options field is variable
- Used to make the total header size correct
- Version - 4 bits (i.e., bits 1 - 4)
- Encapsulation
- IP datagram payload = entirety of TCP or UDP packet
- In turn, that entire IP datagram is encapsulated inside an ethernet frame as that frame's data payload/message
- IP datagram
- IP Address Classes
- Address class system
- Method for dividing global IP address into
- Network ID
- Host ID
- Class A
- Network ID specified by 1st octet in IP address
- Host ID specified by next 3 octets
- Range: 0 - 126
- Max number of hosts: 16MM (256 ^ 3)
- Class B
- Network ID specified by 1st 2 octets in IP address
- Host ID specified by 3rd & 4th octets
- Range: 192 - 224
- Max number of hosts: 65K (256 ^ 2)
- Class C
- Network ID specified by 1st 3 octets in IP address
- Host ID specified by 4th octet
- Range: 128 - 191
- Max number of hosts: 256
- Class D
- Range: 224 - 239
- Used for multicasting
- Class E
- Range: 240 - 255
- Unassigned, & only used for testing
- Method for dividing global IP address into
- CDR
- Discussed below
- Set to replace above system
- Address class system
- ARP
- Used to identify the hardware address of a node with a given IP address
- Because IP data packet is wrapped inside an ethernet frame
- Ethernet frames require MAC addresses
- Therefore, IP addresses need to be translated into MAC addresses
- ARP table is a list of IP addresses & the MAC addresses associated with them
- Populating an ARP table
- When the sending network device does not have a MAC address for a destination IP address
- Device broadcasts (i.e., to FF:FF:FF:FF:FF:FF) an ARP Message
- As a broadcast message, all other devices on network receive the message
- The receiving device which recognizes itself as the desired destination returns an ARP Response (i.e., with the appropriate MAC address)
- The IP Address/MAC Address pairing is stored in ARP table in order obviate future broadcast messages for that IP address
- ARP table entries expire after a short amount of time, so as not to become obsolete
- When the sending network device does not have a MAC address for a destination IP address
- Subnetting
- General
- The process of taking a large network & splitting it up into small networks (i.e., subnets)
- Subnets can create a lot of problems for tech support
- Gateway Router
- Entry & exit router for a given network
- Different from core internet routers
- Need for subnetting
- Especially needed for Class A networks, which can host 16MM other networks
- That's too many networks for a gateway router to keep track of
- Each subnet gets its own gateway router
- Subnet masks
- Subnet ID
- A third ID, in addition to network & host IDs
- Found in some of the bits otherwise used for host ID
- A 32-bit number normally written out as 4 octets in decimal
- 2 sections
- A string of 1s, followed by a string of 0s (32 digits in total)
- The string of 1s used to generate subnet ID
- Ergo, the 0s = what to keep when computing a host ID
- Binary numbers & dotted decimal review
- Assume an IP address of 9.100.100.100
- In binary:
- 9 = 1001
- 100 = 110 0100
- However, because an IP address in binary must use all 32 bits these numbers must be pre-pended with 0s
- Therefore, IP address of 9.100.100.100 = 0000 1001.0110 0100.0110 0100.0110 0100
- Example 1
- Common subnet mask = 255.255.255.0
- In binary, this = 1111 1111.1111 1111.1111 1111.0000 0000
- When applied to IP address of 9.100.100.100
- We know that the 9 = the network ID = 1st octet
- Because our subnet mask has 8 trailing 0s, this means the last 8 bits of the IP address = the host ID
- In dotted decimal notation, this means the 4th octet = the host ID
- Note re subnets:
- An 8-bit number has 256 possibilities
- In practice, though, there are only 254 possible subnets
- 0 is never used
- 255 is reserved for broadcast messages
- Ergo, hosts have numbers 1 - 254 available (not 0 - 255)
- Example 2
- Subnet mask = 255.255.255.224
- In binary, 224 = 1110 0000
- The 5 0s => 32 available host addresses (i.e., 2 ^ 5)
- Subnet mask shorthand
- Indicate with a slash & then the number of 1s in the subnet mask
- This gets appended to the IP address
- Therefore, with a subnet mask of 255.255.255.224 could write an IP address as 9.100.100.100/27
- Subnet ID
- Binary math
- In computers/binary
- 1 = True
- 0 = False
- AND & OR (i.e., logical) operators
- 1 AND 1 = 1
- 1 AND 0 = 0
- 0 AND 0 = 0
- 1 OR 0 = 1
- Etc.
- AND operators are used to determine whether an IP address is on the same network
- In computers/binary
- CIDR
- Limitations of IP addresses & subnetting
- A class C network can only have 254 hosts, which is often too small for a corporate network
- However, a class B network, which allows 65K hosts, is typically many more hosts than needed
- Lastly, the 16MM hosts available in a class A network is generally an unworkable number for a routing table
- In practice
- Companies would set up multiple class C networks
- However, these networks would all be routed to the same place
- How CIDR works
- Abandons concept of network classes
- Network ID & subnet ID are combined into single ID
- Example
- IP address = 9.100.100.100
- Subnet mask = 255.255.255.0
- CIDR Notation
- This is the slash notation shown above- e.g., 9.100.100.100/24
- This in turn means our network address is 9.100.100
- This whole system allows for arbitrary network sizes
- Standard, Class C network
- You have 254 possible hosts (i.e., 256 minus the 0 & 255 ids)
- Combing 2 Class C networks then gives you 508 possible hosts
- With a /23 network
- Network leaves 9 host bits
- 2^9 = 512.
- Drop the 0 & 512, and you have 510 possible hosts
- Standard, Class C network
- Limitations of IP addresses & subnetting
- General
- Routing
- General
- Router
- A network device that forwards traffic using the destination address of that traffic
- Must have:
- At least 2 interfaces
- A routing table!
- Determines destination by looking up IP address in routing table
- Routers, IP addresses, & MAC addresses
- A router has only 1 MAC address
- IP addresses, though, belong to networks, not to hardware
- By definition, a router is connected to at least 2 networks
- Ergo, a router has min 2 IP addresses (1 for each network)
- By and large, most routing in the world is handled by ISPs
- How a router works
- Receives data packet
- Strips (but saves) data-link layer encapsulation - i.e., ethernet header
- This leaves router with the encapsulated IP datagram
- Obtains destination IP (found in IP datagram header)
- Digs into its routing table to:
- Determines destination network
- Determines best router/gateway for sending data packet
- Example:
- Destination IP = 10.0.0.10
- Routing table says that the network to which the router should send data is 10.0.0.0/24
- Rebuilds ethernet frame, while
- Decrementing TTL field
- Calculating new checksum value
- Replacing MAC addresses
- Overwrites source MAC address with its own out-going port MAC address
- Destination MAC address
- If the router is directly connected to the ultimate network of the destination device the router will have the MAC address of that device
- Otherwise, it uses the MAC address of the next router to which the data is being sent
- Receives data packet
- Notes:
- This overall process is repeated at each router through which the data packet is passed
- Gateway routers are normally connected to dozens of networks
- Gateway IP routers
- Normally connected in a mesh
- This gives a sending router many options for where to send a message
- Router
- Routing tables
- Minimum columns
- Destination network
- Could be stored in CIDR notation in a single column
- Will need 2 columns if storing IP and subnet masks separately
- Routing tables typically have a catch-all address for any IP addresses for which it does not know the network IP
- Next hop
- The router to which a message must be forwarded for a given destination
- Will also indicate if the router is directly connected to the destination's network
- Total hops
- The total number of hops required to send a message to its destination
- Note:
- The ideal path to any given destination can change, & sometimes rapidly
- This can be the result of
- Intermediate routers going down
- New routers coming on
- New info
- Interface
- Interfaces
- Provide network connectivity
- A router typically has several
- Types
- Permanent
- Transient (can be configured)
- Networking
- Services
- Container
- Elements
- Location - i.e., slot for
- FPC
- DPC
- MPC
- Cards
- PIC
- MIC
- Interface type, e.g.:
- SONET/SDH
- ATM
- Ethernet
- Encapsulation type
- Misc. interface specific properties
- Location - i.e., slot for
- This column indicates through which interface messages should be sent, given the destination router
- Interfaces
- Destination network
- Core internet routers will have millions of rows in their routing tables
- Minimum columns
- Interior gateway protocols
- Terminology
- Autonomous system
- A collection of networks that are controlled by a single network operator
- Examples:
- A large corporation, which typically controls a number of LANs
- The routers controlled by an ISP
- Routing protocols
- Those used by routers to share info
- This info goes into routing tables
- Autonomous system
- Interior gateway protocols are those used by routers within a single, autonomous system
- 2 types
- Distance-vector protocols
- Older standard
- Vector - in computer science this is another term for a list
- Routing table will contain
- Every network known to router
- The number of hops it will take to reach each such network
- This protocol consists of each router on the network sharing its info about hops with every other router on the network
- Routers don't know about the overall state of its network; only know what each neighbor knows
- Example
- Router A needs to send a message to network X
- Router A is directly connected to Router C and Router B
- Router C has 'declared' that it can reach network X in 3 hops
- Router B has 'declared' that it can reach network X in 2 hops
- Router A will then send the message to Router B
- Link state routing protocols
- Each router shares info about the state of the link of each of its interfaces
- These links could be connections to
- Other routers
- Direct connections to networks
- Each router on system shares its info with every other router on system
- Ergo, each router has much more info about the system
- Complicated algorithms are used to crunch this data to determine optimal path for sending messages
- Distance-vector protocols
- Terminology
- Exterior gateway protocols
- Used for the exchange of info between routers on the edges of independent autonomous systems
- IANA
- Non-profit org that helps manage things like IP address allocation
- Also responsible for ASN system
- ASNs are numbers assigned to individual autonomous systems
- Like IP addresses, ASNs are 32-bit numbers
- However, not split out - expressed as a single decimal number
- Rational
- IP addresses need to represent both a network & host portion
- This is easier to accomplish by splitting IP addresses into 4 bytes
- IP addresses often read by humans - ASNs not so much
- An in-depth understanding of exterior gateway protocols only needed if you work for an ISP
- Non-routable address space
- Background
- There are 4.3B possible IP addresses (2 ^ 32)
- Not enough for 7.5B people on the planet, plus huge data centers
- RFC 1918 published in 1996
- Result was non-routable address spaces
- Not every computer on internet needs to make itself available internet-wide
- Gateways will not forward traffic to networks of non-routable address spaces
- Implications
- Anyone & everyone can use these addresses
- No limit on the number of people using such addresses for internal networks
- Defined 3 address ranges that will never be routed anything by core routers
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- Interior gateways will handle these address ranges
- Result was non-routable address spaces
- Background
- General
- General
- Transport & Application Layers
- Transport Layer
- General
- Transport layer allows traffic to be directed to specific network applications
- Unique layer because it handles both multiplexing & demultiplexing
- Multiplexing - enables nodes on a network to direct traffic to many different receiving services
- Demultiplexing - takes traffic pointed to a single node & directs to proper receiving services
- Ports
- A 16-bit number that's used to direct traffic to specific services running on a networked computer
- Services
- A program on a computer that is waiting to be asked for data
- Service listens for requests on a specific port
- Sockets (aka socket addresses)
- Ports are typically identified by appending to an IP address, separated with a colon (e.g., 10.1.1.100:80)
- Ports identified this way are called sockets
- Common port assignments
- 80 for http requests
- 21 for FTP requests
- With multiplexing, demultiplexing, & ports a single server can host multiple services (e.g., web, mail, ftp)
- TCP segments
- Encapsulated within an IP datagram
- Has a header & a data section
- Data section is where application layer message goes
- TCP header components (& bit sizes)
- Source port (16)
- A high-numbered port chosen from a special section of ephemeral ports
- Must keep multiple out-going connections separate
- Destination port (16) - port of service that traffic is intended for
- Sequence number (32)
- Tracks where in a sequence of TCP segments this little puppy goes
- Because of ethernet frame size limitations messages get split up a lot
- Acknowledgment number (32) - sequence number of next expected segment
- Data offset (4)
- Length of header
- Needed so receiving node knows where data payload begins
- Empty (6)
- Control flags (6)
- There are 6 of them
- See TCP control flags & the three-way handshake, below
- TCP window (16)
- Specifies range of sequence numbers that might be sent before an acknowledgement is required
- TCP relies very heavily on acknowledgements
- Checksum (16)
- Operates same as in ethernet & IP levels
- Run against entire TCP segment
- Urgent pointer (16)
- Used in conjunction with one of the control flags
- Identifies segments which might be more important than other
- Has never really been adopted
- Options (16 if any; otherwise 0)
- Not used very often
- When it is used generally works with more complicated flow-control protocols
- Padding (varies) - to ensure that data payload begins where it is expected to begin
- Data payload (varies)
- Source port (16)
- TCP control flags & the three-way handshake
- Unlike lower-level network layers, TCP is designed to maintain long-running connections
- The 6 TCP control flags
- URG
- A value of 1 => the segment is urgent
- In this case the Urgent Pointer field has more info
- As mentioned above, neither widely adopted nor normally used
- ACK - value of 1 => the acknowledgement number field should be examined
- PSH
- Indicates that the transmitting device wants the receiving device to push any buffered data to the receiving app ASAP
- Used when sending a small, but important, amount of data - & that data is needed for whatever might be in the receiving device's buffer
- RST
- One of the sides in a TCP connection hasn't been able to properly recover from a series of missing or malformed segments
- It's a signal that the communication needs to be restarted from scratch
- SYN
- Used when first establishing a TCP connection
- Tells the receiving device to examine the sequence number field
- FIN
- A value of 1 => there are no more data to be transmitted
- Ergo, the connection can be closed
- URG
- Three-Way Handshake
- Steps (assume Computer A is initiating conversation with Computer B):
- Computer A sends TCP segment with control flag of SYN
- Establishes connection
- Marks where the conversation starts
- Computer B responds with a TCP segment with both SYN & ACK flags set
- Computer A responds with a segment with the ACK flag set
- Computer A sends TCP segment with control flag of SYN
- Handshake - a way for 2 devices to ensure that they are utilizing the same protocol & will therefore be able to understand each other
- Steps (assume Computer A is initiating conversation with Computer B):
- Once 3-way handshake is successfully completed
- Computers have a 'working' connection, & begin sending data
- Work in full duplex mode
- Any message sent by either side needs to be acknowledged with a TCP segment with ACK field sent
- Four-Way Handshake
- Employed to terminate a connection
- Steps (assume Computer B is ending conversation with Computer A):
- FIN flag sent by Computer B
- Computer A sends
- An ACK flag
- A FIN flag (assuming that it is ready to end the connection, which is almost always the case)
- ACK flag sent by Computer B
- Simplex mode
- This is the result of only one side terminating a connection
- Not a situation encountered very often
- TCP socket states
- General
- Socket
- The instantiation of an end-point in a potential TCP connection
- Requires programs to instantiate them
- Port vs. socket
- Note: a port is a virtual/descriptive entity
- You can send traffic to any port you want
- Will only get a response if a program has opened a socket on that port
- States in which sockets can exist
- LISTEN
- Socket is ready & listening for incoming connections
- Seen only on server side
- SYN_SENT
- A synchronization request has been sent, but no connection has yet been established
- Seen only on client side
- SYN_RECEIVED
- A socket previously in LISTEN state has received a synchronization request & has responded with a SYN/ACK message
- However, has not received final ACK message
- Server side only
- ESTABLISHED
- TCP connection has now been (wait for it…) established
- Seen on both server & client sides
- FIN_WAIT
- FIN request has been sent, but an ACK has not yet been received
- Seen on both server & client sides
- CLOSE_WAIT
- Connection has been closed at the TCP layer
- However, the app which originally opened the socket has not yet released the socket
- Seen on both server & client sides
- CLOSED
- Connection has been fully terminated
- Ergo, no further communication is possible
- LISTEN
- Notes:
- Other states can exist
- Because socket states are outside of the scope of TCP itself, nomenclature can vary from OS to OS
- Socket
- General
- Connection-oriented & connectionless protocols
- Connection-oriented protocols
- Establish connections, & use those to ensure that all data have been properly transmitted
- Both sides of a connection always know:
- Which data have been successfully transmitted
- Which have not
- When data transmission issues arise:
- Messages are simply discarded at data link (i.e., ethernet) or network (i.e., IP) layer when checksums fail
- Transport layer protocol determines whether to resend data
- Retransmitting data
- When this occurs messages will obviously not be received in sequential order
- Sequence numbers are what allow 'scrambled' messages to be reconstructed successfully
- Connectionless protocols
- Connection-oriented protocols have a lot of overhead
- Lots of back & forth acknowledgement
- Have to open & tear down connections
- UDP
- Unlike TCP, UDP does not:
- Rely on connections
- Recognize acknowledgments
- You simply:
- Identify a destination port
- Send a packet
- Streaming video is a perfect example of traffic which can (& does) utilize UDP
- Here, a UDP datagram = single frame of a video
- No terrible harm if individual frames get dropped along the way
- Upside of UDP is that without the overhead of establishing connections & acknowledging transmission more bandwidth available for raw data
- Unlike TCP, UDP does not:
- Connection-oriented protocols have a lot of overhead
- Connection-oriented protocols
- Firewalls
- A device that blocks traffic that meets certain criteria
- Can operate at different levels of your network, e.g.:
- Inspect application layer traffic
- Block designated IP addresses (usually ranges)
- Most commonly, firewalls operate at the transportation layer
- Will allow traffic to certain ports, while blocking traffic to other ports
- In typical small business case where one machine works as both web & file server you set up firewall to
- Allow traffic to port 80
- Block external traffic targeting any other port
- Firewall location
- Can be a stand-alone physical device
- However, better to think of a firewall as a program
- As a program a firewall can reside anywhere
- For small companies & home users the firewall 'sits' in the router
- Can also run on indiv host machines/servers
- General
- Application Layer
- General
- Allows applications to communicate with each other in a meaningful manner
- Multiple protocols - HTTP, SMTP, etc.
- OSI model
- Most rigorously defined network model
- Often used in:
- Academia
- Various network certification orgs
- Adds 2 layers between application & transport layer:
- Presentation
- Makes sure the unencapsulated app layer data can be understood by the relevant app
- This is where OS handles data encryption &/or compression
- Session
- Facilitates the communication between apps & the transport layer
- Responsible for passing unencapsulated data to presentation layer
- Because neither layer is performing any encapsulation the 5-layer model treats these layers as part of app layer
- Presentation
- How the layers work together (see Computer Networking Complete Course - Beginner to Advanced, 2:24:45 - 2:36:00, for full example)
- General
- Transport Layer
- Networking Services
- Name Resolution
- DNS
- Global & highly distributed network service that resolves 'English' URLs into numerical IP addresses
- Domain Name is simply something that can be resolved by DNS
- Advantages of DNS
- Easier for humans to remember names than numeric IP addresses
- IP addresses for a domain name can & do change frequently
- Web sites with global audiences
- Generally have multiple web servers around the globe
- DNS allows web sites to be resolved to IPs based on geographic region from which the web site is being accessed
- Accessing web servers that are physically close makes for a faster internet
- Steps in name resolution
- Nodes on a network must have at least the following items configured:
- IP Address
- Subnet mask
- Gateway Router for host
- DNS server
- Technically, a network node does not need this
- However, user would then be restricted to using numeric IP addresses
- Types of DNS servers
- Caching
- Generally provided by your ISP
- Designed to store known domain name lookups for a certain amount of time
- This allows you to circumvent the multi-step (& time-consuming) process of resolving a host name
- TTL
- A value, in seconds, of how long a name server can cache an entry before it must perform a full name resolution again
- Value is configured by domain owner & stored as part of DNS entries
- Several years ago TTLs worked out to be days, or longer, in order to reduce network traffic
- With greater & faster bandwidth TTLs now equate to minutes or, perhaps, hours
- Note: some domain names may still be configured with day-plus TTLs
- To make things even more efficient
- Phones & PCs typically have their own, internal cache of DNS records
- This means that these devices often do not even have to check with a caching server
- Recursive
- Like caching DNS servers, typically provided by your ISP
- Provides full DNS resolution requests
- Full DNS resolution requests are, of course, always performed when the name server has no IP address for a given name
- Root
- There are 13 such 'servers' (in point of fact, there are 13 root authorities, each, of course, with multiple servers)
- Responsible for forwarding name resolution request to appropriate TLD server
- Connecting to a root server
- Formerly, you were assigned one of the root servers - generally the closest geographically
- Anycast
- Current approach
- A technique to route traffic to different destinations based on factors like:
- Location
- Congestion
- Link health
- TLD
- Top hierarchy in DNS name resolution system
- TLD servers correspond to domains, e.g.:
- .com
- .gov
- .biz
- etc.
- Of course, each TLD 'server' is, in fact, a network of servers
- Traffic is routed to appropriate physical server via Anycast
- Authoritative
- Handles domain name lookups for the last 2 parts of a domain name
- For example, somewhere there is an Authoritative name server for
williamhwhite.biz
- Notes:
- Any single, physical DNS server can serve more than one of these roles at once
- Typically, a local name server:
- Performs both caching & recursive roles
- Listens on port 53
- Name servers typically serve an entire network
- Suppose user A types
www.williamhwhite.biz
into browser - Assuming there is no record of this site in the local DNS server the recursive server:
- Resolves this request to the appropriate IP address
- Stores the name resolution in the Cache server
- If, 5 minutes later, user B types
www.williamhwhite.biz
into browser:- The name resolution will be handled by the Cache server
- Suppose user A types
- Caching
- Details of a full, recursive name resolution
- Caching/Recursive name server contacts root name server
- Root name server responds with which TLD server should be queried
- TLD responds with which Authoritative server should be contacted
- Authoritative server then responds to local name server with IP address of the requested web site
- Advantages of the multi-step approach to name resolution
- Makes it much harder to hijack network traffic
- Computers, after all, will direct traffic to whatever IP address it is told to use
- Nodes on a network must have at least the following items configured:
- DNS & UDP
- DNS uses UDP for transport layer
- DNS requests typically can fit into a single datagram
- DNS requests generate a lot of traffic
- With the 5 layers/servers behind the DNS system, using TCP would generate tons of SYN, ACK, etc. back & forth
- In fact, using TCP would generate minimum 44 packets
- 11 packets between each server
- 3-way handshake (packets) to open connection
- 1 packet for actual request
- ACK returned for that request
- Response to the request
- ACK returned for the response
- 4-way handshake (packets) ending the connection
- With UDP, you only have 8 packets for a fully recursive DNS request (request/response between the servers)
- UDP, of course, has no error-checking
- Could well be failures in the process
- In that case the original machine simply re-requests
- DNS & TCP
- TCP is, in fact, used sometimes
- This occurs when there is a complex DNS look-up requested
- When the requested info cannot fit into a single datagram
- That name server informs the requesting machine
- Things then move to TCP
- DNS uses UDP for transport layer
- DNS
- Name Resolution in Practice
- Resource record types
- Dozens of types, though many are for very specialized uses
- Most common types
- A record
- Points a domain name to a specific Ipv4 IP address
- Any given A record applies to a single domain name
- Round Robin
- This is where you iterate over a list one by one
- Comes into play with highly-used domain names
- You create multiple A records for a given domain name
- Do this when you have multiple IP addresses hosting the same domain name
- Example:
- Due to overwhelming interest in www.rcpconsulting.biz (one can dream, can't one?) a certain developer establishes 4 servers to host the site:
- 10.1.1.1
- 10.1.1.2
- 10.1.1.3
- 10.1.1.4
- When a user types
www.rcpconsulting.biz
into browser- DNS name server returns all 4 IP addresses, in the above order
- Browser will first attempt to contact 10.1.1.1
- If there are problems reaching that IP address it will next try 10.1.1.2, etc.
- DNS server then re-orders IP addresses
- 10.1.1.1 drops to bottom of list
- Next user to browse to
www.rcpconsulting.biz
will then be directed first to 10.1.1.2
- This reordering of the IP addresses continues ad infinitum
- Due to overwhelming interest in www.rcpconsulting.biz (one can dream, can't one?) a certain developer establishes 4 servers to host the site:
- Net effect of Round Robin is to distribute load of web requests fairly equally over the number of IP addresses in the list
- AAAA record
- Very similar to A record
- However, returns an IPv6 address
- CNAME record
- Used to re-direct traffic from one domain name to another
- A very common use is to direct, say,
rcpconsulting.biz
(i.e., no "www" prefix) requests to www.rcpconsulting.biz - Could accomplish same thing by setting up 2 A records to handle this situation
- However, the A record approach is both less efficient & more error prone
- Especially becomes an issue with large corporations that might well have established dozens of re-directions
- MX record
- Points to machine handling email
- Large companies often have separate machines for web servers & mail servers
- SRV record
- Similar to MX record type
- Defines locations of specific services (other than email)
- Example: calendar/scheduling service
- TXT record
- Originally designed to supply human-readable info on a web address
- Now, used for additional technical data for computers to read/process
- Typical use is to provide configuration preferences for services that a 3rd-party will handle for your domain
- Field is entirely free-form
- A record
- Domain name anatomy
- 3 parts
- TLD
- The final part of a domain name - e.g.,
.com, .net
, etc. - Many TLDs are quite crowded
- Now have a growing number of vanity TLDs - e.g.,
.museum, .pizza
- ICANN
- Sister organization to IANA
- The 2 organizations define & control the global:
- DNS system
- IP spaces
- The final part of a domain name - e.g.,
- Domain
- Used to demarcate where control moves from a TLD name server to an authoritative name server
- The "rcpconsulting" of www.rcpconsulting.biz
- Registrar
- A company which has an agreement with ICANN to sell unregistered domain names
- A registrar, in turn, sells domain names to John Q. Public
- Subdomain
- The "www" portion of www.rcpconsulting.biz
- Sometimes referred to as host name if assigned to only 1 host
- FQDN - the combination of the 3 parts - i.e., the full www.rcpconsulting.biz
- Subdomains can be freely chosen by anyone who controls a domain
- Can have multiple subdomains
- Example: host.sub.sub.subdomain.rcpconsulting.biz
- In fact, DNS can technically support up to 127 levels of domain in total for a single fully qualified domain name
- TLD
- Length restrictions on domain names
- 63 characters for any 1 section
- 255 characters for FQDN
- 3 parts
- DNS zones
- Hierarchical
- Allows for easier control over multiple levels of a domain
- Network admins control DNS zones
- Layers
- As with subdomains, can create deep layers
- In practice, never more than a few
- Authoritative name servers
- Again, responsible for domain name resolution requests for specific domains
- However, also responsible for DNS zones
- Root servers are responsible for a root zone
- Root & TLD name servers are just special cases of authoritative name servers
- Zones do not overlap
- There will be one authoritative name server for ".biz" TLD
- There will be a separate authoritative name server for "rcpconsulting.biz"
- DNS records
- Typically employ/introduce DNS zones when you have a large number of DNS records
- For example:
- Assume RCP Consulting, LLC has hundreds of employees between NY & NJ (c'mon, work with me on this…)
- Could split into 2 subdomains/zones:
ny.rcpconsulting.biz
nj.rcpconsulting.biz
- This would require 3 authoritative name servers
- One for www.rcpconsulting.biz
- One each for the 2 subdomains
- Zone files
- This is how DNS zones are managed
- Fairly simple files which specify all resource records for a given zone
- Required entries
- SOA record
- Identifies zone
- Specifies authoritative name server responsible for the zone
- NS Records - for other (back-up) name servers that might be responsible for the zone
- SOA record
- Other optional (likely?) records
- A, AAAA, CNAME
- Configuration specifications, like TTL
- Reverse look-up zone files
- Allows DNS servers to send out an IP address rather than a domain name
- FQDN then returned
- Where these files exist
- Will have neither A nor AAAA records
- Will instead have PTRs, which resolve IP addresses to FQDNs
- Hierarchical
- Resource record types
- DHCP
- General
- An application layer protocol that automates the configuration process of hosts on a network
- When a machine connects to a network:
- Pings the DHCP server
- The server then configures the machine
- Importance
- All machines on a TCP/IP network need the following configured:
- Name server
- Gateway
- Subnet mask
- IP address
- First 3 are generally the same for all machines on a network
- IP addresses, however
- Must be unique to each machine
- This gets to be a nightmare to manage manually with a large number of machines
- All machines on a TCP/IP network need the following configured:
- Assigning IP addresses
- Servers & gateways need static & known IP addresses
- With majority of nodes on a network, though, all that is important is that the machine is on the right network
- Options for setting up DHCP server
- Dynamic allocation
- Most common
- A range of IP addresses is set aside for usage by network devices
- A node is assigned one of these addresses whenever requested
- A machine's IP can change every time it logs onto a network
- Automatic allocation
- Again, a range of IP addresses is set aside for usage by network devices
- However
- Server keeps track of IP addresses assigned to a machine in the past
- When possible, assigns the same IP address
- Fixed allocation
- DHCP server maintains a list of MAC addresses and their corresponding IP addresses
- If a MAC address cannot be found in database, server can
- Utilize dynamic or automatic allocation
- Potentially refuse to assign an IP address at all
- Advantage is that only known machines can connect to network
- Dynamic allocation
- Other functions for DHCP servers
- Assign name servers, subnet masks, & gateways
- Assign NTP servers (these keep the clocks of all computers on a network synchronized)
- Using DHCP servers
- General
- DHCP is an application layer protocol
- As such, it depends on all lower layers (transport, network, etc.)
- Conundrum
- DHCP configures the network layer
- However, the network layer is 'missing' for DHCP
- Further issue with client needing to connect to a DHCP server is that the client does not yet:
- Have an IP address
- Know the IP address of the DHCP server
- Solution:
- Client & DHCP server communicate via broadcast messages
- Client broadcasts from UDP port 68
- DHCP servers listen on UDP port 67
- Further, client does know, & transmits, its MAC address
- Note:
- As these messages are broadcast, all nodes on the network will hear them
- Between use of port #s & MAC addresses, clients & server will know which messages to respond to & which to ignore
- Client & DHCP server communicate via broadcast messages
- 4 steps/messages
- DHCPDiscovery message
- Sent by client
- UDP datagram (with port #s & MAC address) encapsulated in an IP datagram
- Datagram sent with following addresses:
- Source IP = 0.0.0.0:68
- Destination IP = 255.255.255.255:67
- Upon receipt DHCP server then
- Examines its own configuration to decide what IP, if any, to offer to client
- IP chosen based on appropriate fixed, dynamic, or automatic address allocation
- DHCPOffer message
- Broadcast by DHCP server
- Datagram contains the following:
- Destination IP = 255.255.255.255:68
- Source IP = server's IP, with a source port of 67
- Client's MAC address
- Client will recognize that the broadcast message is intended for itself because of MAC address
- Client decides whether to accept the offered IP address
- A network can have multiple DHCP servers running simultaneously
- Client may be configured to accept IP addresses only within a certain range
- Almost always, though, the IP offered is accepted
- DHCPRequest message
- Broadcast by client if/when it accepts the offered IP address
- Because the offered IP address hasn't yet been 'officially' established the IP addresses of message are:
- Source: 0.0.0.0:68
- Destination = 255.255.255.255:67
- DHCPAck message
- Broadcast by DHCP server
- Datagram contains the following:
- Destination IP = 255.255.255.255:68
- Source IP = server's IP, with a source port of 67
- Client's MAC address
- Client will once again recognize that the message is intended for itself via MAC address
- DHCPLease
- Contained (I believe) in the DHCPAck message
- Contains all the data that client will now use to set up its own network layer configuration
- 'Lease' used in title because:
- The network configuration has an expiration date
- In other words, client will periodically have to renew its dynamic IP address
- When client disconnects from network
- This releases its DHCPLease
- Tells the DHCP server that it can return the client's IP address to pool of available IP addresses
- DHCPDiscovery message
- General
- General
- NAT
- General
- A technique, rather than a defined standard
- Implementation differs by OS
- However, concept is the same regardless
- The process of translating one IP address into another
- Various rationales
- Security
- Preserving limited IPv4 space
- When done for security
- Handled by gateway
- Router or firewall will
- Rewrite the source IP of an outgoing IP datagram
- Retain the authentic source IP for whenever a response is received
- Rewrite that source IP into responses received
- Various rationales
- IP Masquerading
- Hiding a source IP from a destination machine
- Example:
- Client on Network A needs to reach web server on Network B
- Client machine will have its own IP address
- Gateway router will have
- An IP address on Network A
- A different IP address on Network B
- When gateway takes message from client & passes it to web server it replaces the client IP with its own Network A IP as source address
- One-to-Many NAT
- This is where a router handles multiple clients nodes
- Client IP addresses are each replaced with the router's one IP address
- A technique, rather than a defined standard
- NAT & the transport layer
- NAT as just described are techniques applied at network layer
- NAT at transport layer is more complicated
- Assume a router has utilized one-to-many NAT for hundreds or thousands of machines on a network
- This means that router will receive responses for all of these machines, & yet each response will be utilizing the same destination IP
- Port Preservation
- Technique where the port chosen by client is the same port utilized by router
- Outbound ports are chosen by random from among the ephemeral ports
- Range of available ports: 49,152 to 65,535
- Outbound ports are chosen by networking stack of client machine OS
- Port number is recorded by router in a table
- That port number is then the one plugged into the TCP datagram of the router's outgoing message
- If 2 machines happen to use same ephemeral port numbers at roughly the same time the router will simply come up with a random (& unique) port # for one machine
- Port Forwarding
- Technique where specific destination port numbers can be configured to always correspond to a specific node
- Provides complete IP masquerading
- Example:
- Router is connected to a web server with IP address of 10.1.1.5
- Router would then take any client request where the destination port is 80 & send message to 10.1.1.5:80
- Advantage of technique: simplifies how external clients interact with multiple services provided by a single organization
- NAT, non-routable address space, & limits of IPv4
- IANA responsible for distributing IP addresses since 1988
- RIRs - the 5 entities through which IANA distributes IP addresses
- AFRINIC
- Serves continent of Africa
- Ran out of IPv4 addresses in
- ARIN
- Serves US, Canada, & parts of the Caribbean
- Ran out of IPv4 addresses in
- APNIC
- Serves most of Asia, Australia, New Zealand, & various Pacific island nations
- Ran out of IPv4 addresses in
- LACNIC
- Serves Central & South America, plus balance of Caribbean
- Ran out of IPv4 addresses in
- RIPE
- Serves Europe, Russia, Middle East, & portions of central Asia
- Ran out of IPv4 addresses in
- AFRINIC
- IANA distributed last available IPv4 addresses to RIRs on
- IPv4 exhaustion: the issue of having run out of IPv4 addresses
- General
- VPNs & Proxies
- VPNs
- VPNs
- Name Resolution
- Connecting to the Internet
- Bibliography
- Google. "Computer Networking Complete Course - Beginner to Advanced" YouTube video; Geek's Lesson channel., 5:19:27, February 17, 2019
- Router Interfaces Overview (Juniper Networks TechLibrary)
- General
- Computer Networking (in progress)
single-click
folders to expand/contract by one level
Ctrl+click
folders to expand/contract all (sub-) levels