1. Windows Services
- Create Windows Service (that will just write to a text log)
- How to specify credentials for Windows service
2. Generics
- Generics with classes
- Create new item with generics
- Create list based on generic type
3. Dynamic
4. Enums
- Enum conversion
5. Immutable class
6. Assembly
- Architecture types
- Private XML element for assembly reference
- Mixed mode assembly
7. C#: memory management
- Garbagge collection
- Appearance of list in memory
- GC roots
- Memory leaks
- Resources
- GC modes
- GC.Collect()
8. Extension methods
9. Tracing/Logging
- Trace
- Tracer in app.config
- Trace with filter
- Tracing switch
- Event Log
- Logging with log4net
- Output current class and method (for logging)
10. Configuration
- Get value from default config
- Set value in default config
- Get, add and modify values in specific config file
- Use properties for configuration
- Custom section in app.config - simple
- Custom section in app.config - complex
11. Registry
12. Messages
- Create message queue
- Read messages synchronously
- Read messages asynchronously
13. Activator
- Create a generic class based on dynamic type
14. SecureString
15. Events
- Event aggregator with Caliburn
- Event aggregator with Prism
16. Miscellaneous
- Lazy
- Convert string to byte[] and back
- What is x?.propertyOfX
- typeof vs GetType()
- Switch based on type
- Check class inheritance with .Is??() methods
- Fluent interface
1. Windows Services
Create Windows Service (that will just write to a text log)
Walktrough
In Visual Studio 2010, create new project, Windows group, Windows service
in Service1.cs, OnStart(string[] args) {...} add an action that the service will start. The method has to return in 30 seconds.C# Debug.Listeners.Add(new TextWriterTraceListener(System.IO.File.Create(@"c:\_delete\trace.txt"))); Trace.AutoFlush = true; Trace.WriteLine("starting");Similar way implement OnStop(), OnShutdown)(), OnPause(), OnContinue() Add installer: in Solution explorer, Doubleclick on Service1.cs in Service1 designer, right click and select "Add Installer" doubleclick on ProjectInstaller.cs Right click on serviceInstaller1, select Properties, set Service Name = "MyService", Start Type="Automatic" if you want to service to start automatically Right click on serviceProcessInstaller1, select Properties, set Account = "LocalSystem" in order to avoid entering user name and password Install: InstallUtil.exe WindowsServiceBasics.exe Start: in start menu type "services.msc", find MyService, right click Start Note: you do not have to install the service again when you recompile the binary. Unistall: InstallUtil.exe /u WindowsServiceBasics.exe Note: You need to close the management console window (service is marked for deletion) Debugging: Start the service (see above) From Visual Studio (running as Administrator), menu Debug, Attach to process Click "Show processes from all users" Select process that belongs to the service, click Attach
How to specify credentials for Windows service
C# // ProjectInstaller.Designer.cs private void InitializeComponents() { ... this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; this.serviceProcessInstaller1.Password = null; this.serviceProcessInstaller1.Username = null; ... }2. Configuration services.msc right click on the service tab LogOn, enter user and password
2. Generics
Code example:Basics.zip [Misc.cs]
Resource: csharp-online.net
Available in .net 2.0
namespace System.Collections.Generics
Generics with classes
C# private class PersonLite { internal int Id; internal string Name; } private class Person : PersonLite { internal DateTime BirthDay; } internal static void ShowBasics() { PersonLite personLite = new PersonLite(); Populate(personLite); Person person = new Person(); Populate(person); person.BirthDay = DateTime.Parse("1969-01-01"); } private static void Populate<T>(T p) where T : PersonLite { // read data from external source, e.g. DB p.Id = 1; p.Name = "John"; }
Create new item with generics
C# private class PersonLite { internal int Id; internal string Name; } private class Person : PersonLite { internal DateTime BirthDay; } internal static void DemoCreateNewObject() { PersonLite personLite = CreateNew<PersonLite>(); Person person = CreateNew<Person>(); } private static T CreateNew<T>() where T : PersonLite, new() { T result = new T(); result.Name = "new"; return result; }
Create list based on generic type
C# private class PersonLite { internal int Id; internal string Name; } private class Person : PersonLite { internal DateTime BirthDay; } internal static void DemoLoadDataBasedOnType() { List<PersonLite> list = CreateList<PersonLite>(); List<Person> list2 = CreateList<Person>(); } private static List<T> CreateList<T>() where T : PersonLite, new() // new() is needed New<T>() { List<T> result = new List<T>(); Type t = typeof(T); int count = 1; switch (t.Name) { // this can be used to generate a SQL query case "PersonLite": count = 2; break; case "Person": count = 3; break; } for (int i = 0; i < count; i++) { T item = new T(); item.Name = "new"; result.Add(item); } return result; }
3. Dynamic
C# DateTime date = new DateTime(); // date.AddCentury(1); fails at compile time dynamic dateDynamic = new DateTime(); dateDynamic.AddCentury(1); // fails at runtime var obj = date as dynamic; obj.AddCentury(); // fails at runtime
4. Enums
Enum conversion
C# public enum Department { IT, Finance } string text = Enum.GetName(IT.GetType(), IT); Department d = (Department) Enum.Parse(typeof(Department), "IT");
5. Immutable class
Problem: when a class is passed as an argument to a method, its content can be unexpectedly modified.
How to prevent that: Classes can be ensured to be immutable by using readonly keyword for their members.
6. Assembly
Architecture types
corflags myassembly.exe
Any CPU: PE = PE32 and 32BIT = 0 x86: PE = PE32 and 32BIT = 1 64-bit: PE = PE32+ and 32BIT = 0
Private XML element for assembly reference
XML <Reference Include="myLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..., processorArchitecture=MSIL"> <Private>True</Private> </Reference>Identifies if that the assembly file will be copied to the output (e.g. bin) folder. If not specified, build task will decide.
Mixed mode assembly
An existing application consisting entirely of unmanaged functions can be brought to the .NET platform by recompiling just one module with the /clr compiler switch
7. C#: memory management
Garbagge collection
Large Object Heap - Objects > 85k or multidimensinonal arrays
There are 3 small Object Heaps: Generation 0, 1 and 2
The idea is that objects created recently are more likely to be released quickly
Small objects are allocated on Gen 0 heap.
When Gen 0 is full (256k), GC runs and objects that cannot be disposed, so called survivors, are moved to Gen 1
When Gen 1 is full (2M), GC runs on Gen 1 and moves survivors to Gen 2 and runs GC on Gen 0.
When Gen 2 is full (10M), full GC runs on Gen1 and then Gen 0. If insuficiant memory, then OutOfMemory exception is thrown.
Objects on Large than 85k are allocated on Object Heap (LOH) and are collected when full (Gen 2) collection takes place. LOH is never compacted, it fragments over time.
If there isn't enough memory to allocate new large objects, OutOfMemory exception is thrown.
The garbage collector calculates whether or not an object can be disposed from one of the heaps by creating a graph, which shows how the object is referenced by other objects.
Appearance of list in memory
Arrays with 10,000 or more end up on LOH.
GC roots
Static variables: always considered GC roots
Managed object passed to COM+ through interop: Ends when COM reference counter is set to 0.
Object with finalizer: If it is no longer live, it becomes special kind of GC root until .net called the finalizer (placed in finalizer queue). Require more than one collection GC. Object with finalizers cause delay to be released from memory, use Dispose() whenever possible and GC.SuppressFinalize().
Memory leaks
Solution:
- use local rather than global variable
- call Dispose() or use "using(...){ }" concept
- set variable to null when it is not needed anymore
Failing to release unmanaged resources
- practises of deallocating unmanaged code apply
Failing to dispose Drawing objects
e.g Bitmaps, brushes, fonts - these are managed objects, but they hold references to unmanaged objects. These resources are cleaned up when the objects are disposed
Solving LOH fragmentation:
- Split list or arrays into smaller objects that remain below 85kB
- allocate the largest and longest-living objects first
- let GC compact the LOH (.net > 4.5.1). If GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce the LOH is compacted during the next full blocking garbage collection, and the property value is reset to default.
- restart program
Generally: do no rely on finalization. 1. It happens unpredictably and may take long time, 2. Finalizer of certain object may not call Dispose() (if it has a bug)
Resources
64 system can page out memory, which decreases performance, and event run out disk space.
GC modes
Server (ASP.net apps): NET will suspend the running application while the garbage collector is running.
GC.Collect()
8. Extension methods
Code example:Basics.zip [ExtensionMethods.cs]
Extension methods allow to extend classes that are marked as sealed.
More detail at MSDN
C# sealed class SealedClass { internal void DoSomething() { Console.WriteLine("SealedClass.DoSomething()"); } // cannot be called from extension method protected void DoSomethingProtected() { Console.WriteLine("SealedClass.DoSomethingProtected()"); } internal static void DoSomethingStatic() { Console.WriteLine("SealedClass.DoSomethingStatic()"); } } static class ExtensionClass { internal static void DoSomething(this SealedClass sealedClass) { Console.WriteLine("Gets never called"); } internal static void DoSomething(this SealedClass sealedClass, int i) { sealedClass.DoSomething(); // class method can be called
// sealedClass.DoSomethingProtected(); not available Console.WriteLine("ExtensionClass.DoSomething(int i)"); } internal static void DoSomethingElse(this SealedClass sealedClass) { Console.WriteLine("ExtensionClass.DoSomethingElse()"); } internal static void DoSomethingStatic(this SealedClass sealedClass) { Console.WriteLine("Gets never called"); } } class ExtensionMethodsDemo { internal static void DemoExtensionMethods() { SealedClass sealedClass = new SealedClass(); sealedClass.DoSomething(); // calls SealedClass sealedClass.DoSomething(1); // calls ExtensionClass sealedClass.DoSomethingElse(); // calls ExtensionClass SealedClass.DoSomethingStatic(); // calls SealedClass } }
9. Tracing/Logging
Code example:Basics.zip [Logging.cs]
Trace
Tracing Options
C# // trace to a text file TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME); textWriterTraceListener.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.ProcessId | TraceOptions.ThreadId; Trace.Listeners.Add(textWriterTraceListener); // trace to the console Trace.Listeners.Add(new ConsoleTraceListener()); // trace to event log // !! this code must be executed als administrator when the source Basics.exe does not exist ventLogTraceListener eventLogTraceListener = new EventLogTraceListener("Basics.exe"); Trace.WriteLine("Written by Trace.WriteLine()"); Trace.Flush(); Trace.AutoFlush = true; Trace.TraceInformation("Written by Trace.TraceInformation"); // writes datetime + process id + thread id Trace.TraceWarning("Written by Trace.TraceWarning"); Trace.TraceError("Written by Trace.TraceError");
Tracer in app.config
XML <configuration> <system.diagnostics> <trace autoflush="true"> <listeners> <remove name="Default"/> <add name="textWriterTraceListenerConfig" type="System.Diagnostics.TextWriterTraceListener" traceOutputOptions="DateTime,Timestamp,Callstack,LogicalOperationStack,ProcessId,ThreadId" initializeData="MyLogFromConfig.log"> </add> </listeners> </trace> </system.diagnostics> </configuration>
Trace with filter
C# TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME); EventTypeFilter e = new EventTypeFilter(SourceLevels.Error); // .Error: Verbose, Information, Warning are ignored textWriterTraceListener.Filter = e; Trace.Listeners.Add(textWriterTraceListener); Trace.TraceWarning("DemoEventTraceWithFilter - Trace warning"); // will be ignored (by filter) Trace.TraceError("DemoEventTraceWithFilter - Trace error"); // will be writtenThis can be also done in app.config
XML <add name="textWriterTraceListenerConfig" type="System.Diagnostics.TextWriterTraceListener" initializeData="MyLogFromConfig.log"> <filter type="System.Diagnostics.EventTypeFilter" initializeData="Warning"/> </add>
Tracing switch
C# TraceSwitch traceSwitch = new TraceSwitch("textWriterTraceListener", "description" ); traceSwitch.Level = TraceLevel.Warning; // Off 0, Error 1, Warning 2 default, Info 3, Verbose 4 TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME); Trace.Listeners.Add(textWriterTraceListener); Trace.WriteLine("switch controlled - no IF"); Trace.WriteLineIf(traceSwitch.TraceVerbose, "switch controlled verbose"); // will be ignored Trace.WriteLineIf(traceSwitch.TraceError, "switch controlled error"); Trace.Flush();or in app.config
XML <system.diagnostics> <switches> <add name="textWriterTraceListener" value="3"/> </switches> ...
Event Log
.Net provides EventLog class that can be used to write to the log.
Windows provides Application, Security and System logs.
C# string myAppName = "Basics3.exe"; // event source name must be unique accross all logs // Write to application log
EventLog eventLog = new EventLog("Application", Environment.MachineName, myAppName); EventLog.WriteEntry(myAppName, "Demo error", EventLogEntryType.Error); // !! this code must be executed als administrator when the source does not exist // Write to custom log EventLog customEventLog = new EventLog("ZbynekDemo1", Environment.MachineName, myAppName + "Custom"); customEventLog.WriteEntry("Demo error", EventLogEntryType.Error); // first time you have to close and open Event Viewer to see the new custom event log
Logging with log4net
configuration
Install log4net NuGet package (log4net.dll will be added to project references)
C# using log4net; using log4net.Config; namespace Log4NetBasics { class Program { private static readonly ILog _logger = LogManager.GetLogger("MyLogger"); static void Main(string[] args) { XmlConfigurator.Configure(); // read the configuration from app.config // BasicConfigurator.Configure(); // ! does not read configuration for File or Debug appenders _logger.Info("Info message"); _logger.Warn("Warning message"); _logger.Error("Error message"); Console.ReadLine(); } } }
XML <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <log4net> <appender name="Console" type="log4net.Appender.ColoredConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> </layout> </appender> <root> <level value="ALL"/> <appender-ref ref="Console"/> </root> </log4net> </configuration>If you want the logger to add information like datetime or thread id, you need to use conversionPattern element
XML <log4net> <appender name="Console" type="log4net.Appender.ColoredConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger %property{myData} (%file:%line): %message %newline"/> </layout> </appender> ... </log4net>will produce:
2016-05-22 11:00:05,887 [10] ERROR MyLogger Abc (c:\Log4NetBasics\Program.cs:18): Error message%property{myData} will pint out content set as below
C# GlobalContext.Properties["myData"] = "abc";If you want the messages in console colored, you can define mapping between the severity and the color
XML <log4net> <appender name="Console" type="log4net.Appender.ColoredConsoleAppender"> <mapping> <level value="ERROR"/> <foreColor value="Red"/> </mapping> ... </appender> ... </log4net>Adding another listeners, called appenders, can be done the same way in app.config
XML <log4net> <appender name="File" type="log4net.Appender.RollingFileAppender"> <param name="File" value="c:\\temp\\log4netBasics.log"/> <param name="AppendToFile" value="true"/> <maximumFileSize value="100KB" /> <maxSizeRollBackups value="2" /> <layout type="log4net.Layout.PatternLayout"></layout> </appender> <appender name="DebuggerAppender" type="log4net.Appender.DebugAppender"> <layout type="log4net.Layout.PatternLayout"> </layout> </appender> ... </log4net>
Output current class and method (for logging)
C# private static void Log(string message) { var frame = new StackFrame(skipFrames: 1); // 1 will get the frame of the caller of the Log method System.Reflection.MethodBase callingMethod = frame.GetMethod(); Console.WriteLine("Class: {0} Method: {1}", callingMethod.DeclaringType.Name, callingMethod); }
10. Configuration
Code example:Basics.zip [Configuration.cs]
Get value from default config
XML <configuration> <appSettings> <add key="key1" value="value1" /> </appSettings> </configuration>the values can be retrieved as
C#
string settingValue = System.Configuration.ConfigurationManager.AppSettings["key1"];
Set value in default config
C#
try
{
System.Configuration.ConfigurationManager.AppSettings.Add("added", "added value");
}
catch (ConfigurationErrorsException ex)
{
Console.WriteLine(ex);
// "The configuration is write protected" exception. When executed from VS or Basics.exe.
}
but you can open the default app.config as a specific file and then you can modify the values.
Get, add and modify values in specific config file
C# ExeConfigurationFileMap map = new System.Configuration.ExeConfigurationFileMap(); map.ExeConfigFilename = "app1.config"; // this can be used for Basics.exe.config or Basics.vshost.exe.config as well Configuration mappedConfiguration = ConfigurationManager.OpenMappedExeConfiguration(map, System.Configuration.ConfigurationUserLevel.None); AppSettingsSection appSettingsSection = (AppSettingsSection)mappedConfiguration.GetSection("appSettings"); // Get string settings = appSettingsSection.Settings["key2"].Value; // Add appSettingsSection.Settings.Add("addedKey", "addedValue"); mappedConfiguration.Save(); // Remove appSettingsSection.Settings.Remove("addedKey"); mappedConfiguration.Save();
Use properties for configuration
Another way how to store configuration data is to use Properties.Settings class.
- The settings are stored in app.config either in userSettings or applicationSettings sections.
XML <configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="Basics.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="Basics.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </sectionGroup> </configSections> <userSettings> <Basics.Properties.Settings> <setting name="MySettingForUser" serializeAs="String"> <value>user</value> </setting> </Basics.Properties.Settings> </userSettings> <applicationSettings> <Basics.Properties.Settings> <setting name="MySettingForApp" serializeAs="String"> <value>app</value> </setting> </Basics.Properties.Settings> </applicationSettings> </configuration>- The settings need to be created in the Settings tab in Project properties.
After the creation the Properties\Settings.settings file will be modified.
C# internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { [global::System.Configuration.UserScopedSettingAttribute()] public string MySettingForUser { get { return ((string)(this["MySettingForUser"])); } set { this["MySettingForUser"] = value; } } }The settings can be accessed with the code below:
C# // get application scope setting value string value = Properties.Settings.Default.MySettingForApp; // Properties.Settings.Default.SettingApp = value + "x"; does not compile, SettingApp has no setter // get and set user scope setting value string userValue = Properties.Settings.Default.MySettingForUser; Properties.Settings.Default.MySettingForUser = userValue + "+"; Properties.Settings.Default.Save();
Custom section in app.config - simple
C# class ConfigurationElementSimple : System.Configuration.ConfigurationSection { [System.Configuration.ConfigurationProperty("myValue", IsRequired = true)] public string MyValue { get { return (string)base["myValue"]; } set { base["myValue"] = value; } } [System.Configuration.ConfigurationProperty("myNumber", IsRequired = false)] public int MyNumber { get { return (int)base["myNumber"]; } set { base["myNumber"] = value; } } }Define the section and populate its values in app.config. configSections must be the first section in the app.config. The same section type can be used multiple times.
XML <configuration> <configSections> <section name="simpleCustomSection" type="Basics.ConfigurationElementSimple, Basics"/> <section name="simpleCustomSection2" type="Basics.ConfigurationElementSimple, Basics"/> </configSections> <simpleCustomSection myValue="abc" myNumber="1" /> <configuration>Read content of the section in code.
C# ConfigurationElementSimple config = (ConfigurationElementSimple)System.Configuration.ConfigurationManager.GetSection("simpleCustomSection"); config = (ConfigurationElementSimple)System.Configuration.ConfigurationManager.GetSection("simpleCustomSection2");
Custom section in app.config - complex
C# public class ConfigurationCollectionItem : ConfigurationElement { [ConfigurationProperty("name", IsRequired = true, IsKey = true)] public string Name { get { return (string)base["name"]; } } [ConfigurationProperty("detail", IsRequired = false, IsKey = true)] public string Detail { get { return (string)base["detail"]; } } public string UniqueName { get { return string.Format("{0}{1}", Name, Detail); } } } public class MyConfigurationCollection : ConfigurationElementCollection { public ConfigurationCollection() { } protected override ConfigurationElement CreateNewElement() { return new ConfigurationCollectionItem(); } protected override Object GetElementKey(ConfigurationElement element) { return ((ConfigurationCollectionItem)element).UniqueName; } } public class ConfigurationElementComplex : ConfigurationSection { [ConfigurationProperty("id", IsDefaultCollection = false, IsRequired = true)] public string Id { get { return (string)base["id"]; } } [ConfigurationProperty("myList", IsDefaultCollection = false, IsRequired = true)] public MyConfigurationCollection MyList { get { return (MyConfigurationCollection)base["myList"]; } } }Define the section and populate its values in app.config. configSections must be the first section in the app.config.
XML <configuration> <configSections> <section name="complexCustomSection" type="Basics.ConfigurationElementComplex, Basics"/> </configSections> <complexCustomSection id="1"> <myList> <add name="John" detail="abc" /> <add name="Paul" detail="def" /> </myList> </complexCustomSection> <configuration>Read content of the section in code
C# ConfigurationElementComplex config1 = (ConfigurationElementComplex)System.Configuration.ConfigurationManager.GetSection("complexCustomSection"); // string s = ((ConfigurationCollectionItem)config1.MyList[0]).Name; // access not allowed (config1.MyList[0]) foreach(var item in config1.MyList) { string name = ((ConfigurationCollectionItem)item).Name; }
11. Registry
Code example:Basics.zip [Registry.cs]
Registry class is located unter Microsoft.Win32 namespace and allows manupulation records in Windows registry system.
C# // create or overwrite registry value Registry.SetValue(Registry.CurrentUser.Name + @"\Software\Zbynek Cernin\Basics", "TestValue", "abc"); // read registry value string value = (string)Registry.GetValue(Registry.CurrentUser.Name + @"\Software\Zbynek Cernin\Basics", "TestValue", string.Empty); // read registry value - using RegistryKey RegistryKey basicsKey = Registry.CurrentUser.OpenSubKey(@"Software\Zbynek Cernin\Basics"); if (basicsKey != null) { value = (string)basicsKey.GetValue("TestValue"); value = (string)basicsKey.GetValue("TestValue1"); // does not exists - returns null } // test if values exists and delete it
basicsKey = Registry.CurrentUser.OpenSubKey(@"Software\Zbynek Cernin\Basics", writable: true); if (basicsKey != null) { if (basicsKey.GetValueNames().Where(s => s == "TestValue").Count() > 0) { basicsKey.DeleteValue("TestValue"); } }
12. Messages
Code example:Basics.zip [Messages.cs]
Windows messages can be used as communication mean to communicate between applications.
Windows messages are not enabled by default, you have to go to Control Panel > Programs and Features > Enable Windows Features and click on "Microsoft Message Queue Server".
Create message queue
C# // this code has to run as AdminAfter succesful execution the queue will be visible in Computer management, under Services and Application, Message Queuing, Private queues
System.Messages.MessageQueue.Create(@".\Private$\MyQueue");
Read messages synchronously
C# if (MessageQueue.Exists(@".\Private$\MyQueue")) { MessageQueue mq = new MessageQueue(@".\Private$\MyQueue"); // send mq.Send("Hello " + DateTime.Now.ToString(), "Title"); // receive Message msg = mq.Receive(); };
Read messages asynchronously
C# static void WriteAndReadMessageAsynchronously() { MessageQueue mq = new MessageQueue(@".\Private$\MyQueue"); mq.Send("Hello " + DateTime.Now.ToString(), "Title"); mq.ReceiveCompleted += new ReceiveCompletedEventHandler(MessageReceived); mq.BeginReceive(); } public static void MessageReceived(object source, ReceiveCompletedEventArgs args) { MessageQueue mq = (MessageQueue)source; Message msg = mq.EndReceive(args.AsyncResult); string body = (string)msg.Body; }
13. Activator
Code example:Basics.zip [Misc.cs]
Activator class allows creating instance of a class from its type name
C# internal class Person { internal int ID; internal string Name; } // arguments: assembly name, full class name with namespace Person p = new Person() { ID = 1 }; object p1 = Activator.CreateInstance("Basics", "Basics.Person").Unwrap(); p = (Person)p1;
Create a generic class based on dynamic type
Assuming set of classes as data entities
C# public class BaseClass { public int id; } public class Person : BaseClass { public string FirstName; public string LastName; } public class Department : BaseClass { public string DepartmentName; }and generic converter
C# interface IEntityConverter { BaseClass Deserialize(string data); } public class EntityConverter<T> : IEntityConverter where T : BaseClass { public BaseClass Deserialize(string data) { string debug = typeof(T).ToString(); // see the type to demo that correct type is used return new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<T>(data); // from System.Web.Extensions } }it is possible to create a generic converter class based on dynamicly specified type as a string
C# private static IEntityConverter CreateGenericConverter(string className) { // get full dto object name for given entity name string fullPersonClassName = typeof(Person).FullName; // use Person class to get namespace string nameSpace = fullPersonClassName.Substring(0, fullPersonClassName.LastIndexOf('.')); string fullClassName = nameSpace + "." + className; // get executing assembly name string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; // construct dto for given entity BaseClass classInstance = (BaseClass)Activator.CreateInstance(assemblyName, fullClassName).Unwrap(); // construct generic converter for given entity dto Type genericConverterType = typeof(EntityConverter<>); Type constructedConverterForType = genericConverterType.MakeGenericType(classInstance.GetType()); object entityConverter = Activator.CreateInstance(constructedConverterForType); return (IEntityConverter)entityConverter; }and it can be used as followed
C# IEntityConverter departmentConverter = CreateGenericConverter("Department"); string sObj = "{\"DepartmentName\":\"IT\",\"id\":1}"; BaseClass obj = departmentConverter.Deserialize(sObj);
14. SecureString
It is not possible to compare value of 2 SecureStrings directly.
C# System.Security.SecureString NewPassword; System.Security.SecureString NewPassword2; NewPassword.Clear(); NewPassword.AppendChar('a'); NewPassword2.Clear(); NewPassword2.AppendChar('a'); bool b = NewPassword2.Equals(NewPassword); // b is FALSE b = NewPassword.GetHashCode() == NewPassword.GetHashCode(); // b is FALSEIf security allows, you can convert SecureString to string
C# IntPtr sPtr = IntPtr.Zero; try { sPtr = Marshal.SecureStringToBSTR(securePassword); String s1 = Marshal.PtrToStringBSTR(sPtr); StringBuilder sb = new StringBuilder(); for (int i = 0; i < s1.Length; i++) { sb.Append(s1[i]); } return sb.ToString(); } finally { if (ss_bstr1_ptr != IntPtr.Zero) { Marshal.ZeroFreeBSTR(ss_bstr1_ptr); } }and compare strings.
15. Events
Event aggregator with Caliburn
Caliburn EventAggregator simplifies event dispatching and subscribing. Instead of standard types like strings an integer you can send custom classes with parameters.
C# // add reference to Caliburn.micro.dll using Caliburn.Micro; // this is needed in order to see PublishOnUIThread extension method public partial class MainForm : Form { internal static readonly Caliburn.Micro.IEventAggregator _eventAggregator = new Caliburn.Micro.EventAggregator(); public void Publish() { _eventAggregator.PublishOnUIThread("Hello from Caliburn"); _eventAggregator.PublishOnUIThread(123); } } public partial class Form2Calibrun : Form, IHandle<string>, IHandle<int> { public Form2Calibrun() { ... MainForm._eventAggregator.Subscribe(this); } public new void Handle(string message) { label1.Text = (string)message; } public new void Handle(int message) { label1.Text = Convert.ToString(message); } }
Event aggregator with Prism
It will reference to Prism.dll
C# // define event object public class MyStringEvent : PubSubEvent<string> { } // string event public class Person { public int Id; public string Name; } public class MyPersonEvent : PubSubEvent<Person> { } / // create aggregator class IEventAggregator _aggregator = new EventAggregator(); // subscribe events _aggregator.GetEvent<MyStringEvent>().Subscribe(StringEventHandler); _aggregator.GetEvent<MyPersonEvent>().Subscribe(PersonEventHandler); // subscribe events with filter SubscriptionToken unsubscribeToken = _aggregator.GetEvent<MyStringEvent>() .Subscribe(StringEventHandler, ThreadOption.UIThread, keepSubscriberReferenceAlive: false, filter: s => s.StartsWith("A")); // handle events private void StringEventHandler(string s) { ... } private void PersonEventHandler(Person p) { ... } // raise event _aggregator.GetEvent<MyStringEvent>().Publish("Hello"); _aggregator.GetEvent<MyPersonEvent>().Publish(new Person() { Name = "John"} ); // unsubscribe _aggregator.GetEvent<MyStringEvent>().Unsubscribe(StringEventHandler); _aggregator.GetEvent<MyStringEvent>().Unsubscribe(unsubscribeToken);
16. Miscellaneous
Lazy
C# public sealed class MySingleton { private static readonly Lazy<MySingleton> lazy = new Lazy<MySingleton>(() => new MySingleton()); public static MySingleton Instance { get { return lazy.Value; } } private MySingleton() { } }Alternatives without Lazy
Convert string to byte[] and back
C# byte[] bytes = Encoding.ASCII.GetBytes(someString);
string someString = Encoding.ASCII.GetString(bytes);
What is x?.propertyOfX
C# string bar = x?.PropertyOfX;is equivalent of
C# string bar = (x == null ? null : x.PropertyOfX);
typeof vs GetType()
GetType() is a method you call on individual objects
C# string s = "hi"; typeof(string) == s.GetType() // true string obj = "hello"; typeof(object) == s.GetType() // false
Switch based on type
C# Object o = 3; Type t = o.GetType(); var mySwitch = new Dictionary<Type, Action> { { typeof(int), () => { Console.Write("int"); } }, { typeof(string), () => { Console.Write("string"); } }, }; if (mySwitch.ContainsKey(t)) mySwitch[t]();
Check class inheritance with .Is??() methods
C# typeof(Derived).IsSubclassOf(typeof(Base)) // true typeof(Base).IsSubclassOf(typeof(Base)) // false typeof(Base).IsAssignableFrom(typeof(Derived)) // true typeof(Base).IsAssignableFrom(typeof(Base)) // true typeof(int[]).IsAssignableFrom(typeof(uint[])) // true - but not a subclass
Fluent interface
In C# fluent interface is implemented in LINQ using extension methods.
C# // fluent var query1 = arr.Where(x => x > 7).OrderBy(x => x); // progressive var query2a = arr.Where(x => x > 7); var query2b = query2a.OrderBy(x => x);