EditChapter 8 - C# 4.0 New Language Features
EditC# 4.0 Features
EditListing 8-1 : COM Interop and Optional Parameters.
This sample shows how optional parameters make calling Excel using COM-Interop easier.
public void Listing_8_1_COMInteropWithAndWithoutOptionalParameters()
{
string fileName = Path.Combine(
Environment.CurrentDirectory, "Data/SampleExcel.xlsx");
// Old way – before optional parameters
var excel = new Microsoft.Office.Interop.Excel.Application();
try
{
Microsoft.Office.Interop.Excel.Workbook workBook =
excel.Workbooks.Open(fileName, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Type.Missing);
// do work with Excel...
workBook.Close(false, fileName);
}
finally
{
excel.Quit();
}
// New Way – Using optional parameters
var excelNew = new Microsoft.Office.Interop.Excel.Application();
try
{
Microsoft.Office.Interop.Excel.Workbook workBook =
excelNew.Workbooks.Open(fileName);
// do work with Excel...
workBook.Close(false, fileName);
}
finally
{
excelNew.Quit();
}
}
Top
EditListing 8-2 : Optional Parameters.
This sample shows how to call methods using optional parameters.
public void Listing_8_2_OptionalParameters()
{
OldWay oldWay = new OldWay();
oldWay.DoSomething(
"({0},{1}) Old way – defaults from overloaded methods.");
NewWay newWay = new NewWay();
// the following statement are logically identical
// (except for the string value passed in)
newWay.DoSomething(
"({0},{1}) New way – defaults in param definitions.");
newWay.DoSomething(
"({0},{1}) New way – formatString and param1 passed in only.",
0);
newWay.DoSomething(
"({0},{1}) New way – formatString, param1 and param2 passed in.",
0,
true);
}
public class OldWay
{
// multiple overloads call the one master
// implementation of a method that handles all inputs
public void DoSomething(string formatString)
{
// passing 0 as param1 default,
// and true as param2 default.
DoSomething(formatString, 0, true);
}
public void DoSomething(string formatString, int param1)
{
DoSomething(formatString, param1, true);
}
public void DoSomething(string formatString, bool param2)
{
DoSomething(formatString, 0, param2);
}
// the actual implementation. All variations call this
// method to implement the methods function.
public void DoSomething(
string formatString,
int param1,
bool param2)
{
Console.WriteLine(
String.Format(formatString, param1, param2));
}
}
public class NewWay
{
// optional parameters have a default specified.
// optional parameters must come after normal params.
public void DoSomething(
string formatString,
int param1 = 0,
bool param2 = true)
{
Console.WriteLine(
String.Format(formatString, param1, param2));
}
public void M1(string s, int i = 1) { }
//public void M2(System.Drawing.Point p = new System.Drawing.Point()) { }
public void M3(System.Drawing.Point p = default(System.Drawing.Point)) { }
public void M4(int i = 1, params string[] values) { }
/* ERRORS –
//"Optional parameters must appear after all required parameters"
public void M1 (int i = 1, string s) {}
//"Default parameter value for 'p' must be a compile–time constant"
//Can't use a constructor that has parameters.
public void M2(Point p = new Point(0,0)) {}
//"Default parameter value for 'p' must be a compile–time constant"
//(Must be a value type (struct or built–in value types only))
public void M5(StringBuilder p = new StringBuilder()) {}
//"A ref or out parameter cannot have a default value"
public void M6(int i = 1, out string s = "") {}
//"Cannot specify a default value for a parameter array"
public void M7(int i = 1, params string[] values = "test") {}
*/
}
Console output (Execution time: 0ms):
[
Hide/Show]
(0,True) Old way – defaults from overloaded methods.
(0,True) New way – defaults in param definitions.
(0,True) New way – formatString and param1 passed in only.
(0,True) New way – formatString, param1 and param2 passed in.
Top
EditListing 8-3 : Named Arguments.
This sample shows how to call methods using named arguments.
public void Listing_8_3_NamedArguments()
{
// reversing the order of arguments.
Point p1 = new Point(y: 100, x: 10);
// using an expression as the argument value.
int width = 600;
Point p2 = new Point(y: width – 100, x: 10);
/* ERRORS
//"Named argument 'x' specifies a parameter for which a
// positional argument has already been given"
Point p3 = new Point(10, x: 10);
// "Named argument specifications must appear after all
// fixed arguments have been specified"
Point p4 = new Point(y: 100, 10);
// "The best overload for '.ctor' does not have a
// parameter named 'x'"
Point p5 = new Point(x: 10);
*
*/
// skipping and reordering arguments
NewWay newWay = new NewWay();
// skipping an optional parameter
newWay.DoSomething(
"({0},{1}) New way – param1 skipped.",
param2: false);
// any order, but if it doesn't have a default
// it must be specified by name somewhere!
newWay.DoSomething(
param2: false,
formatString: "({0},{1}) New way – params specified" +
" by name, in any order.",
param1: 5);
}
public class NewWay
{
// optional parameters have a default specified.
// optional parameters must come after normal params.
public void DoSomething(
string formatString,
int param1 = 0,
bool param2 = true)
{
Console.WriteLine(
String.Format(formatString, param1, param2));
}
public void M1(string s, int i = 1) { }
//public void M2(System.Drawing.Point p = new System.Drawing.Point()) { }
public void M3(System.Drawing.Point p = default(System.Drawing.Point)) { }
public void M4(int i = 1, params string[] values) { }
/* ERRORS –
//"Optional parameters must appear after all required parameters"
public void M1 (int i = 1, string s) {}
//"Default parameter value for 'p' must be a compile–time constant"
//Can't use a constructor that has parameters.
public void M2(Point p = new Point(0,0)) {}
//"Default parameter value for 'p' must be a compile–time constant"
//(Must be a value type (struct or built–in value types only))
public void M5(StringBuilder p = new StringBuilder()) {}
//"A ref or out parameter cannot have a default value"
public void M6(int i = 1, out string s = "") {}
//"Cannot specify a default value for a parameter array"
public void M7(int i = 1, params string[] values = "test") {}
*/
}
Console output (Execution time: 0ms):
[
Hide/Show]
(0,False) New way – param1 skipped.
(5,False) New way – params specified by name, in any order.
Top
EditListing 8-4 : Named Arguments and Optional Parameters in LINQ Queries.
This sample shows how to use named arguments and optional parameters in a LINQ query.
public void Listing_8_4_NamedAndOptionalLINQParameters()
{
var q = from c in Contact.SampleData()
where c.State == "CA"
select new Contact(
c.FirstName, c.LastName,
state: c.State,
dateOfBirth: c.DateOfBirth
);
foreach (var c in q)
Console.WriteLine("{0}, {1} ({2}) – {3}",
c.LastName, c.FirstName,
c.DateOfBirth.ToShortDateString(), c.State);
}
public class Contact
{
// Constructor defined with optional arguments
public Contact(
string firstName,
string lastName,
DateTime dateOfBirth,
string email = "", // optional
string phone = "", // optional
string state = "Other") // optional
{
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
Email = email;
Phone = phone;
State = state;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public DateTime DateOfBirth { get; set; }
public string State { get; set; }
public static List<Contact> SampleData()
{
return new List<Contact> {
new Contact ("Barney", "Gottshall", new DateTime(1945,10,19), phone: "885 983 8858", email: "bgottshall@aspiring–technology.com", state: "CA"),
new Contact ("Armando", "Valdes", new DateTime(1973,12,09), phone: "848 553 8487", email: "val1@aspiring–technology.com", state: "WA" ),
new Contact ("Adam", "Gauwain", new DateTime(1959,10,03), phone: "115 999 1154", email: "adamg@aspiring–technology.com", state: "AK" ),
new Contact ("Jeffery", "Deane", new DateTime(1950,12,16), phone: "677 602 6774", email: "jeff.deane@aspiring–technology.com", state: "CA" ),
new Contact ("Collin", "Zeeman", new DateTime(1935,02,10), phone: "603 303 6030", email: "czeeman@aspiring–technology.com", state: "FL" ),
new Contact ("Stewart", "Kagel", new DateTime(1950,02,20), phone: "546 607 5462", email: "kagels@aspiring–technology.com", state: "WA" ),
new Contact ("Chance", "Lard", new DateTime(1951,10,21), phone: "278 918 2789", email: "lard@aspiring–technology.com", state: "WA" ),
new Contact ("Blaine", "Reifsteck", new DateTime(1946,05,18), phone: "715 920 7157", email: "blaine@aspiring–technology.com", state: "TX" ),
new Contact ("Mack", "Kamph", new DateTime(1977,09,17), phone: "364 202 3644", email: "mack.kamph@aspiring–technology.com", state: "TX" ),
new Contact ("Ariel", "Hazelgrove", new DateTime(1922,05,23), phone: "165 737 1656", email: "arielh@aspiring–technology.com", state: "OR" )
};
}
}
Console output (Execution time: 29ms):
[
Hide/Show]
Gottshall, Barney (10/19/1945) – CA
Deane, Jeffery (12/16/1950) – CA
Top
EditListing 8-7 : Dynamic type to parse CSV file line.
Class to represent a dynamic type that will allow the LINQ code (or any other code) to parse a single comma-separated line and access data at runtime based on thenames in the header row of the text file.
public void Listing_8_7_CSVLine()
{
// To show the code for CSVLine.
}
public class CsvLine : System.Dynamic.DynamicObject
{
string[] _lineContent;
List<string> _headers;
public CsvLine(string line, List<string> headers)
{
this._lineContent = line.Split(',');
this._headers = headers;
}
public override bool TryGetMember(
GetMemberBinder binder,
out object result )
{
result = null;
// find the index position and get the value
int index = _headers.IndexOf(binder.Name);
if (index >= 0 && index < _lineContent.Length)
{
result = _lineContent[index];
return true;
}
return false;
}
public override bool TryGetIndex(
GetIndexBinder binder,
object[] indexes,
out object result)
{
result = null;
int index = (int)indexes[0];
if (index >= 0 && index < _lineContent.Length)
{
result = _lineContent[index];
return true;
}
return false;
}
}
Top
EditListing 8-8 : Dynamic type to parse CSV files (header and process lines).
The IEnumerable class that reads the header line and returns each line in the content as an instance of our CsvLine dynamic type
public void Listing_8_8_CsvParser()
{
// To show the code for CsvParser.
}
public class CsvParser : IEnumerable
{
List<string> _headers;
string[] _lines;
public CsvParser(string csvContent)
{
_lines = csvContent.Split('\n');
// grab the header row and remember positions
if (_lines.Length > 0)
_headers = _lines[0].Split(',').ToList();
}
public IEnumerator GetEnumerator()
{
// skip the header line
bool header = true;
foreach (var line in _lines)
if (header)
header = false;
else
yield return new CsvLine(line, _headers);
}
}
Top
EditListing 8-9 : LINQ over Dynamic Types.
This sample shows how to use dynamic lookups to parse and code against CSV file data.
public void Listing_8_9_DynamicLookupOnCSVFile()
{
string content =
"FirstName,LastName,State\nTroy,Magennis,TX\nJanet,Doherty,WA";
var q = from dynamic c in new CsvParser(content)
where c.State == "WA"
select c;
foreach (var c in q)
{
Console.WriteLine("{0}, {1} ({2})",
c.LastName,
c.FirstName,
c.State);
}
}
public class CsvLine : System.Dynamic.DynamicObject
{
string[] _lineContent;
List<string> _headers;
public CsvLine(string line, List<string> headers)
{
this._lineContent = line.Split(',');
this._headers = headers;
}
public override bool TryGetMember(
GetMemberBinder binder,
out object result )
{
result = null;
// find the index position and get the value
int index = _headers.IndexOf(binder.Name);
if (index >= 0 && index < _lineContent.Length)
{
result = _lineContent[index];
return true;
}
return false;
}
public override bool TryGetIndex(
GetIndexBinder binder,
object[] indexes,
out object result)
{
result = null;
int index = (int)indexes[0];
if (index >= 0 && index < _lineContent.Length)
{
result = _lineContent[index];
return true;
}
return false;
}
}
public class CsvParser : IEnumerable
{
List<string> _headers;
string[] _lines;
public CsvParser(string csvContent)
{
_lines = csvContent.Split('\n');
// grab the header row and remember positions
if (_lines.Length > 0)
_headers = _lines[0].Split(',').ToList();
}
public IEnumerator GetEnumerator()
{
// skip the header line
bool header = true;
foreach (var line in _lines)
if (header)
header = false;
else
yield return new CsvLine(line, _headers);
}
}
Console output (Execution time: 156ms):
[
Hide/Show]
Top
EditListing 8 : The dynamic Keyword.
This sample shows how to use dynamic keyword in C#.
public void Listing_8_DynamicKeyword()
{
dynamic o = 1;
o.ThisMethodIsNotDefinedAnywhere();
object os = "test";
// ERROR – 'object' does not contain
// a definition for 'ToUpper'
//os.ToUpper();
// we must cast to access the methods
// on the underlying string type.
((string)os).ToUpper();
// var works, but only method scoped,
// and can't be used in a Generic type.
var vs = "test";
vs.ToUpper();
// ERROR – 'var' may only appear within
// a local variable declaration
//List<var> list = new List<var>();
// dynamic work in all cases
dynamic ds = "test";
ds.ToUpper();
List<dynamic> dlist = new List<dynamic>();
}
Console output (Execution time: 36ms):
[
Hide/Show]
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'int' does not contain a definition for 'ThisMethodIsNotDefinedAnywhere'
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid1[T0](CallSite site, T0 arg0)
at SampleQueries.Chapter08Samples.Listing_8_DynamicKeyword() in C:\Users\Troy\Desktop\HOL Book\LINQ to Objects using CSharp 4 Samples\LINQ to Objects using CSharp 4 Sample Queries\Chapter08Samples.cs:line 409
Top
EditListing 8-14 : COM Interop and LINQ.
This sample shows how to use COM interop and LINQ.
public void Listing_8_14_COMInteropLINQ()
{
string filename = Path.Combine(
Environment.CurrentDirectory, "Data/SampleExcel.xlsx");
const int firstNameCol = 0;
const int lastNameCol = 1;
const int stateCol = 5;
var q = from row in GetExcelRowEnumerator(filename, 1)
where row[stateCol] == "WA"
select row;
Console.WriteLine("Customers in WA ({0})", q.Count());
foreach (var row in q)
{
Console.WriteLine("{0}, {1}",
row[lastNameCol].ToUpper(), row[firstNameCol] );
}
}
Console output (Execution time: 1812ms):
[
Hide/Show]
Customers in WA (3)
VALDES, Armando
KAGEL, Stewart
LARD, Chance
Top
EditListing 8 : COM Interop.
This Microsoft sample shows how to use COM interop.
public void Listing_8_COMInterop()
{
// From the Microsoft Samples
var excel = new Microsoft.Office.Interop.Excel.Application();
excel.Visible = true;
excel.Workbooks.Add(); // optional arguments
excel.Cells[1, 1].Value = "Process Name"; // dynamic property set
excel.Cells[1, 2].Value = "Memory Usage"; // dynamic property set
var processes = Process.GetProcesses()
.OrderByDescending(p => p.WorkingSet64)
.Take(10);
int i = 2;
foreach (var p in processes)
{
excel.Cells[i, 1].Value = p.ProcessName; // dynamic property set
excel.Cells[i, 2].Value = p.WorkingSet64; // dynamic property set
i++;
}
Microsoft.Office.Interop.Excel.Range range = excel.Cells[1, 1]; // dynamic conversion
Excel.Chart chart = excel.ActiveWorkbook.Charts.
Add(After: excel.ActiveSheet); // named and optional arguments
chart.ChartWizard(
Source: range.CurrentRegion,
Title: "Memory Usage in " + Environment.MachineName); //named+optional
chart.ChartStyle = 45;
chart.CopyPicture(Excel.XlPictureAppearance.xlScreen,
Excel.XlCopyPictureFormat.xlBitmap,
Excel.XlPictureAppearance.xlScreen);
}
Top