Q: What is the purpose of the module within a .NET assembly?A: A module is a collection of classes that do not have a manifest. This organizational level is used for several reasons: 1. Support generation of a single assembly from several different languages 2. Reduce download footprint for web applications. The CLR downloads only the main module by default; other modules are downloaded as needed. 3. Share small bits of common code that developers don't want to package into a separate assembly. For more information, see this link. Q: How do I execute a .NET assembly class library static method from the debugger, as with rundll32 and a native DLL?A: .NET does not ship with any tool that does this, but it can be accomplished with PowerShell. To do so: 1. Create a file named "powershell.exe.config" in the project directory with the following content: <?xml version="1.0"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0.30319"/> <supportedRuntime version="v2.0.50727"/> </startup> </configuration> 2. Create a file named "RunStub.ps1" in the project directory with the following contents: #requires -version 2.0 # By David Luxford, 2014 August 4 # Validate command line parameters if ($args.Length -eq 4) { $assemblyfilename = $args[0] $namespace = $args[1] $classname = $args[2] $methodname = $args[3] } else { write-output "Method invoker" write-output "Invokes a public static void( void ) method in a .NET assembly." write-output "" $noArgs = $args.Length write-output "Insufficient command line parameters $noArgs, expected 4. Syntax:" write-output "<assemblyFilename> <namespace> <className> <methodName>" exit } # Initialize write-output "Invoking $namespace.$classname.$methodname in $assemblyfilename" #Load the assembly, class, and invoke the type $targetAssembly = [Reflection.Assembly]::LoadFile($assemblyfilename) $targetType = $targetAssembly.GetType("$namespace.$classname") $targetType.InvokeMember("$methodname", 'Public,Static,InvokeMethod,DeclaredOnly', $null, $null, $null)2. In the class library project in Visual Studio, goto Project Properties. 3. Select the Debug tab. 4. Set Start Action to "Start external program", and set the text box to the following <full pathname to bin directory>\powershell.exe 5. Set Command line arguments to the following, replacing <namespace>, <className>, and <methodName> with the actual values. Note assemblyFileFullPathname must be the full pathname to the assembly file in the debug bin directory: -ExecutionPolicy ByPass -File "..\..\DebugStub.ps1" "<assemblyFileFullPathname>" <namespace> <className> <methodName> 6. Select the Build Events tab. 7. Set the Post build event command line to the following: if not exist "$(TargetDir)\powershell.exe" copy "c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe" $(TargetDir) if not exist "$(TargetDir)\powershell.exe.config" copy "$(ProjectDir)\powershell.exe.config" $(TargetDir) Notes: Due to Visual Studio 2012 and below limitations, the paths on the Debug tab are machine specific. If using 2013, limited macros can be used here instead. If symbol server supported is enabled, the first invocation may appear to hang as Visual Studio downloads the symbols for the .NET framework used by powershell.exe. The source for the manifest trick is this link. Q: While debugging a .NET assembly running under IIS, I get the error "Cannot obtain value of local argument as it is not available" in the Locals window. How do I fix this?A: There are several possible causes. Before debugging, goto Project Properties|Debug and set Enable native code debugging. Rebuild the assembly, post the update, then try to debug again. After doing so, if the problem recurs, stop debugging and repost the DLL/PDB. Source is one of the responses to this question. Note that simply enabling the Native code debugger in the Attach to Process window will NOT fix this. Q: In C#, how do I suppress re-generation of the stack trace when I re-throw an exception outside of an exception handler, such as an exception that originated on another thread?A: There are two ways to do this: Use Reflection to call the private method InternalPreserveStackTrace on the exception object before throwing it, as in: private static void PreserveStackTrace(Exception exception) { MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic); preserveStackTrace.Invoke(exception, null); } Courtesy this blog entry. In NET 4.5 and above, recapture the original exception with System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture, then rethrow the resulting object in the new context using it's .Throw method. Courtesy this stack exchange posting. Note this is not quite identical to method #1 as the stack trace text includes a message "End of stack trace from the previous location where the exception was thrown". Q: How do I merge two NET assemblies, like static linking in C++?A: Use the ILMerge tool. NOTE that specifying the correct set of reference assemblies is not obvious. For example, NET 4.5 was deployed as an "in-place" upgrade of 4.0, meaning that it replaces the 4.0 assemblies and otherwise is "fully compatible". However, the later is not true as some classes, such as the hidden ExtensionMethodAttribute class, have been moved to different assemblies. This can cause the ILMerge resultant assembly to fail if used incorrectly. To correct this, specify the authoritative set of reference assemblies for the NET framework version being targeted. These assemblies are installed to C:\Program Files\Reference Assemblies as of .NET 3.0 (2007) for precisely such a purpose. For example, if using ILMerge to create an assembly targetting .NET 4.0 on a machine with .NET 4.5 installed, you must use the following command line options: /targetplatform:"v4,C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" If instead you specify "c:\windows\Microsoft.NET\Framework\v4.0.30319", the resultant assembly will not work on .NET 4.0 because it will built to resolve .NET 4.0 classes in the new .NET 4.5 assemblies, but they are not there. However, the incorrect command line would work fine on a build machine with .NET 4.0 only. Courtesy this blog posting. Q: How do I "force sign" a NET assembly that Visual Studio cannot/will not sign?A: Use ildasm to decompile the built assembly, then ilasm to rebuild it and sign it. For more information, see this web page. Q: ILMerge fails with a unresolved assembly error referencing WindowBase. How do I fix this?A: This is a WPF assembly. Either specify the path to the WPF assemblies via the /lib argument, or use /targetplatform only with a Reference Assemblies path. The following command line demonstrates the first solution, used on a machine with VS2013 which therefore lacks a NET 4.0 Reference Assemblies, so /targetplatform only is not an option "C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe" /ndebug /copyattrs /targetplatform:4,"C:\Windows\Microsoft.NET\Framework64\v4.0.30319" /lib:"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF" /out:"D:\duo_iis\InstallUtil\bin\x64\Debug\InstallUtilMerged.exe" "D:\duo_iis\InstallUtil\bin\x64\Debug\InstallUtil.exe" "D:\duo_iis\InstallUtil\bin\x64\Debug\DuoUtil.dll" Courtesy this blog posting. Q: In .NET, how do I determine if a certificate is valid for use by a web server (HTTPS), as described on the first tab of the certificate details property page?A: A certificate valid for use by a web server (HTTPS) has this usage embedded within the certificate data. In the .NET object model, this is reported in the X509Certificate2's Extensions collection under the Key Usage and Enhanced Key Usage extensions. In X509v2, an extension is a uniquely-identified blob of variable format data where the identifiers use OIDs, a string of period-separated integers such as 2.5.29.15. .NET includes parsers for both of these extensions and automatically parses them if found. To check the Key Usage and Enhanced Key Usage extensions, scan the Extensions collection for objects of type X509KeyUsageExtension and X509EnhancedKeyUsageExtension. To validate X509KeyUsageExtension, check the KeyUsages field. Validating X509EnhancedKeyUsageExtension is more difficult because this is a list of constants that NET does not appear to include. A list of the known usage identifiers can be found at this link, or by searching for information on the Enhanced Key Usage OID 2.5.29.37. The OIDs of the other commonly used extensions are Key Usage, 2.5.29.15, and Basic Constraints, 2.5.29.19. The later is parsed by X509BasicConstraintsExtension. Q: In .NET, how do I read the Subject Alternate Name extension of a certificate?A: This is easy, but unobvious and undocumented. It requires use of the CertEnroll COM objects, which are present on Server 2012.
Source this article. Q: When I #import the TLB from a C# assembly in Visual C++, I get error c2146: syntax error : missing ';' before identifier 'gettype'A: This is due to a reference to a missing definition for _TypePtr that is typically unneeded, and can be resolved by either removing the reference to it or providing the definition. To remove generation of the reference to _TypePtr (recommended): a. In the C# assembly generating the type library, decorate all ComVisible classes with [System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]. To provide the reference for _TypePtr: b. In the C++ client using the type library, add a #import for mscorlib.tlb above the #import for the C# assembly. In either case, a Rebuild All is necessary to fix the build. Courtesy this stack overflow answer. Q: In a Visual Studio C# project, how do I create a COM reference using a fixed filename instead of to a registered object/TLBID TLBID, so I can check in the TLB along with the project and ensure the interop DLL is always generated with registering the object(s) on the machine?A: Use the barely documented COMFileReference MSBuild method. The easiest way to do so is:
Source is this stackoverflow answer. Q: Where are the C# reference assemblies?A: They are in several locations, depending on the age of your Visual Studio version. The most authoritative version is C:\Program Files (x86)\Reference Assemblies. Despite the directory, the assemblies found here are the appropriate for either 32-bit or 64-bit builds. In older times, Microsoft stored the reference assemblies in C:\Program Files\Reference Assemblies. Copies of the NET 3.0 and 3.5 reference assemblies can still be found there. Q: How do I suppress the NET Framework Initialization Error dialog displayed by a C#/.NET EXE that cannot run due to a problem with the NET framework, such as a pending install reboot?A: It can't be done. This message is automatically generated by the framework initialization code and cannot be suppressed from a .NET host. To suppress it, you must write a C++ host and manually initialize the .NET framework. See this MSDN link Q: I can't find a certificate in a store calling .Find(X509FindType.FindByThumbprint that I know is there. Why does this fail?A: The valid parameter of this call requires that the cert be valid AND trusted. If it is not trusted, it will not be found. Change this parameter to FALSE. Q: When I call System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity, it throws a COMException stating "The specified directory service attribute or value does not exist", but the user has full rights. How do I fix this?A: This is due to a bug/unobvious requirement of FindByIdentity were it requires read rights to the Computers group in the forest, normally CN=Computers, in addition to the Users group in order to automatically determine the search context. To resolve this, specify the third argument when creating the PrincipalContext, the container. This should be the DC of the forest or domain where the user account exists, such as "DC=alpha, DC=local". Courtesy this MSDN blog posting. Q: In C#, I get a TypeLoadException "Generic method or method in generic class is internal call pinvoke..."; this used to work. How do I fix this?A: The most likely cause is that one or more DllImports have been added to the class. C# does not allow DllImport declarations in Generic classes. To resolve this, move the DllImport members to a non-generic class. Q: How do I make an HttpWebRequest use TLS 1.1 or 1.2 from a .NET 4.0 assembly?A: This can only be done if .NET 4.5 or higher is installed. Also, if this is a Windows Server machine, it must be later than Server 2008 R0. To do so, manually set System.Net.ServicePointManager.SecurityProtocol to include the TLS 1.1 and 1.2 values. Because these constants are not available when targeting .NET 4.0, you must manually specify them. For example, use System.Enum.TryParse to parse the strings "Tls11" and "Tls12", then OR this onto the SecurityProtocol value. Note that if the FIRST request to the target host does not use TLS 1.2, then neither will subsequent requests - this is a separate issue in the .NET framework. Q: How do I enable NET framework SSL/TLS logging for System.Net APIs programmatically?A: There is no proper API to do so programatically. To do so, you must use reflection to extract the log sources, connect them to a listener, and enable the System.Net debugging flag. To do so:
Q: How do I determine the dependencies of a NET assembly (equivalent of depends tool for DLLs)?A: Follow these instructions:
All assembly references (dependent assemblies) are listed here in this format: AssemblyRef #4 (23000004) ------------------------------------------------------- Token: 0x23000004 Public Key or Token: b0 3f 5f 7f 11 d5 0a 3a Name: System.Web Version: 2.0.0.0 Major Version: 0x00000002 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: <null> HashValue Blob: Flags: [none] (00000000) Q: How do I determine what version of the NET framework is being used by my program?A: This is difficult. First, the framework version is not the same as the CLR, which is the underlying virtual machine executing the framework. In practice, however, since the two are always updated together, they are essentially the same. The problem is that the NET APIs report the CLR version, not the framework version, so it is difficult to differentiate between NET 4.0, 4.5, 4.5.1, etc. It turns out the best way to determine this for an app where you own the process is to get the version of clr.dll, then map the build number it to a table to determine the CLR version. The later part is only necessary for NET 4.0-4.5, as Microsoft stopped this with 4.6. The table is:
Courtesy this MSDN blog posting. Q: How do I write a JSON REST service in WCF?A: There are several ways. One of the easiest is to use WebServiceHost with System.ServiceModel.Web.WebInvoke. To do so: 1. Declare a standard WCF service class 2. Decorate each operation contract with WebInvoke, setting response and request formats to JSON and the appropriate Method. 3. For each operation contract, declare one DataContract class for all return values and one for all parameters. 4. Run Visual Studio as a local admin, or grant rights for non-elevated-admin use of the URL/port via netsh http add urlacl to the user/group that will run this. In the example below, InstanceContextMode == Single is not required. This is done to permit control and configuration of the mock service for testing. /// <summary> /// Mockup of the Duo Auth service /// </summary> [System.ServiceModel.ServiceContract(SessionMode=System.ServiceModel.SessionMode.NotAllowed)] [System.ServiceModel.ServiceBehavior(InstanceContextMode=System.ServiceModel.InstanceContextMode.Single, IncludeExceptionDetailInFaults=true)] public class AuthServiceMock { [System.ServiceModel.OperationContract] [System.ServiceModel.Web.WebInvoke(Method = "GET", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json)] public Duo.DuoResult<Duo.Authentication.ResultTime> ping() { var requestUri = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri; Duo.DuoResult<Duo.Authentication.ResultTime> result = null; result = new Duo.DuoResult<Duo.Authentication.ResultTime>(); result.methodResult = new Authentication.ResultTime(); result.methodResult.utcTime = DateTime.UtcNow; result.methodSucceeded = "OK"; return result; } [DataContract] public class ArgumentsGetCapabilities { [DataMember(Name="ipaddr", IsRequired=true)] public string ipAddress; [DataMember(Name="user_id")] public string userID; [DataMember(Name="username")] public string userName; [DataMember(Name="trusted_device_token")] public string trustedDeviceToken; } [System.ServiceModel.OperationContract] [System.ServiceModel.Web.WebInvoke(Method = "POST", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json, RequestFormat=System.ServiceModel.Web.WebMessageFormat.Json)] public Duo.DuoResult<Duo.Authentication.ResultAuthenticationCapabilities> preauth( ArgumentsGetCapabilities arguments ) { var requestUri = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri; var headers = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.Headers; var result = new Duo.DuoResult<Duo.Authentication.ResultAuthenticationCapabilities>(); result.methodResult = new Authentication.ResultAuthenticationCapabilities(); result.methodResult.statusMessage = "This is the status"; result.methodResult.mobileEnrollmentUrl = new Uri("http://www.nowhere.com"); result.methodSucceeded = "OK"; return result; } /// <summary> /// Starts auth service mockup, reports endpoint to console and runs enter [enter] pressed /// </summary> public static void RunService() { Uri authServiceEndpoint = null; AuthServiceMock actualService; using (var host = CreateAndRunService(out authServiceEndpoint, out actualService)) { Console.WriteLine("The service is ready at {0}", authServiceEndpoint); Console.WriteLine("Press <Enter> to stop the service."); Console.ReadLine(); host.Close(); } } /// <summary> /// Creates an instance of the Duo Auth service mockup and returns it. /// </summary> /// <remarks> /// Caller should dispose when done. /// </remarks> public static System.ServiceModel.Web.WebServiceHost CreateAndRunService( out Uri authEndpoint, out AuthServiceMock runningServiceInstance ) { runningServiceInstance = new AuthServiceMock(); var host = new System.ServiceModel.Web.WebServiceHost(runningServiceInstance); // Bind service contract to an Http REST GET/POST handler // TODO: Use correct hostname for certificate // TODO: Generate cert with appropriate hostname, trust cert, find free port, bind cert to port var localHostname = "localhost"; var baseAddress = new Uri(String.Format("http://{0}:{1}/auth/v2", localHostname, 8085)); var webBinding = new System.ServiceModel.WebHttpBinding(); webBinding.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.None; var serviceRestEndpoint = host.AddServiceEndpoint(typeof(AuthServiceMock), webBinding, baseAddress); host.Open(); authEndpoint = baseAddress; return host; } } Q: When starting my WCF service, I get an error System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL .... Your process does not have access rights to this namespace (see here for details)A: The current process has insufficient rights to receive on that URL. To resolve this, either run the process as an elevated local admin, or start an elevated command prompt and use netsh http add urlacl to grant rights to the user/group that will run this. Answer courtesy this stackoverflow post. Q: How do I create and call a COM object from C#?A: This is easy, but is the opposite of what is normally done. To do so:
Courtesy MSDN and this blog post example for IApplicationAssociationRegistration. Q: How do I return an http error code from a WCF REST method?A: Set the status code and description, then return null for the result. Only the first two are required, but since the result will not be sent, your method should not return a result. For example: [System.ServiceModel.OperationContract] [System.ServiceModel.Web.WebInvoke(Method = "GET", ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json)] public Duo.DuoResult<Duo.Authentication.ResultTime> ping() { var requestUri = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri; Duo.DuoResult<Duo.Authentication.ResultTime> result = null; result = new Duo.DuoResult<Duo.Authentication.ResultTime>(); result.methodResult = new Authentication.ResultTime(); result.methodResult.utcTime = DateTime.UtcNow; result.methodSucceeded = "OK"; bool simulateForbiddenReturn = false; if (simulateForbiddenReturn) { System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.Forbidden; System.ServiceModel.Web.WebOperationContext.Current.OutgoingResponse.StatusDescription = "Dlux test"; return null; } return result; } Q: How do I write a WCF REST POST method that returns JSON but accepts a custom format, not even XML?A: Declare the method as usual with WebInvoke, but write it to accept a single parameter of type Stream. Then, for example, if you expect those bytes to be text as with the Duo Auth API, use a StreamReader to decode them and process them as you need. Also, set the BodyStyle attribute so WCF knows the response is JSON, but the request is raw. For example: [System.ServiceModel.OperationContract] [System.ServiceModel.Web.WebInvoke(Method = "POST", BodyStyle=System.ServiceModel.Web.WebMessageBodyStyle.WrappedResponse)] public Duo.DuoResult<Duo.Authentication.ResultAuthenticationCapabilities> preauth(System.IO.Stream pStream) { var requestUri = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri; var headers = System.ServiceModel.Web.WebOperationContext.Current.IncomingRequest.Headers; var myReader = new System.IO.StreamReader(pStream); var inputText = myReader.ReadToEnd(); var result = new Duo.DuoResult<Duo.Authentication.ResultAuthenticationCapabilities>(); result.methodResult = new Authentication.ResultAuthenticationCapabilities(); result.methodResult.statusMessage = "This is the status"; result.methodResult.mobileEnrollmentUrl = new Uri("http://www.nowhere.com"); result.methodSucceeded = "OK"; return result; } Q: When trying to start a timer using TimeSpan's, I get the error "Number must be either non-negative and less than or equal to Int32.MaxValue or -1."A: You are using TimeSpan.MinValue - use TimeSpan.Zero for 0 instead. Q: How do I write a C++-style destructor to cleanup objects in C#?A: This concept does not quite exactly exist in C# because you can't delete an object, but you can implement IDisposable and a finalizer to accomplish what is intended. Which is more appropriate depends on what is being done. In general, IDisposable is recommended. Do the following: 1. Add a cleaned up bool to the class, typically called disposed, initialized to false. 2. Write a single cleanup function, typically called Dispose(bool), structured as follows: public void Dispose( bool disposing ) { if (!disposed) { // Do all necessary cleanup // If you need to differentiate where the call came from, check disposing. Typically unnecessary // unless you're cleaning up unmanaged stuff when Dispose is called and everything else later. disposed = true; } } 3. Implement IDisposable.Dispose to cleanup and skip the destructor with code like this: Dispose(true); GC.SuppressFinalize(this); 4. Implement a destructor (also called a Finalizer) with the following code: Dispose(false). Q: In C#, how do I get a pointer to a struct/object, so I can work with it's bytes directly as in C++?A: C# does not encourage this - its perspective is that it owns the memory representation of a thing. This can be done for structs using unsafe code, as in the following: unsafe { MyStructType sampleStruct = new MyStructType(); MyStructType* sampleStructManagedPtr = &sampleStruct; byte* sampleStructRawManagedPtr = (byte*) sampleStructManagedPtr; byte[] targetArray = new byte[System.Runtime.InterOp.Marshal.SizeOf(sampleStruct)]; int currTargetByte = 0; for (int currSourceByte = 0; currSourceByte++; currSourceByte < System.Runtime.InterOp.Marshal.SizeOf(sampleStruct)) { targetArray[currTargetByte] = *(sampleStructRawManagedPtr + currSourceByte); currTargetByte += 1; } } Note the above code is simple for purposes of illustration. Anything concerned with speed should use long*'s to do qword (8-byte) transfers. Object memcpys cannot be done via C# as the compiler will report a CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type. However, this could probably be accomplished via IL, but again with the caveat that the runtime owns this representation and so it may change between versions, across platforms, or for other reasons. Q: How do I do a memcpy in C#?A: This is a more complicated questions than it seems. In C#, there are two kinds of memory: managed and unmanaged. All managed code can only work directly with managed memory. a. To do a memcpy between two unmanaged regions, use IntPtr's to refer to the regions and an unmanaged method or Windows DLL export, such as CopyMemory, to move the bytes. b. To do an unmanaged/managed memcpy, use System.Runtime.Interop.Marshal.Copy c. To do a managed-to-managed memcpy, you must implement it yourself by using unsafe pointers. Note that you cannot copy objects as you would in C++, but you can copy blittable types, which are structs or arrays of structs. Alternatively, if you are comfortable writing in IL, the NET version of assembly, there is a cpblk instruction that is the exact equivalent. For performance tests on memcpy methods in NET, see http://code4k.blogspot.com/2010/10/high-performance-memcpy-gotchas-in-c.html. In summary, using DDWORD transfers, performance is similar in x86/x64 modes, although cpblk is much faster in x64 than x86. d. To do a managed-to-managed copy of arrays of primitives in a bytewise fashion or access their byte representation directly, see System.Buffer. Q: In C#, when trying to get the pointer of a generic struct, I get an error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type.A: This can't be done. C# appears to treat generic structs like objects. Make the struct non-generic. Q: How can I inline a C# method?A: There is no way to force a method to be inlined, but you can request a class or method to be inlined. NET has several restrictions on what methods may and may not be inlined. In NET 4, an entire class can be requested to be inlined by marking it with [System.Runtime.TargetedPatchingOptOut]. In NET 4.5, it is possible to override the maximum method size criteria by marking a method with [System.Runtime.MethodImpl(MethodImplOptions.AggressiveInlining)]. The inline exclusion criteria are the following:
The NET 4.5 AgressiveInline attribute overrides only the maximum length exclusion criteria. Q: I get an InvalidCastException when casting a boxed primitive (value type) to another primitive type it is compatible with. Why and how do I fix this?A: Use multiple casts, use System.Convert.To<Primitive>, or change the type of the source object being cast to dynamic instead. This is by design for performance reasons. The C# cast operator is limited to unboxing value types only to their original type, because the code required to always fully analyze an object for conversion is very expensive. The final solution, a dynamic object, is the most powerful, but also the most expensive. For more information, see this blog entry. Q: In C#, when calling a method on an interface implemented by a struct that should change the struct, the struct does not change, but the code runs!A: Change the initial assignment of the struct to use the interface type. C#'s autoboxing clones the original struct in the cast assignment and the method call is made on the autoboxed clone, which is discarded. Consequently, this code - where promote is an IPromotion method that increments a Grade member from 65 to 66, and ToString reports the Grade - reports 65 twice instead of 65, then 66: To resolve this, either change the struct to a class, or change the initial assignment to the interface type (IPromotion employee = new Employee...) so the autoboxing never occurs. Courtesy this MSDN blog. Q: What's the fastest way to identify the currently executing C# method?A: It depends on what you mean and what version of NET is used. If for WPF's INotifyPropertyChanged, use NET 4.5 with C# 5.0's CallerMemberName attribute (see below for more information). If you only need to identify the currently running method, use System.Reflection.MethodBase.GetCurrentMethod(). This is by far the fastest method with a performance penalty of only approximately 35x (one order of magnitude times a small constant). If you need to go above the current stack frame for any reason - for example, to determine the caller to the current method, then the impact is considerably higher, by another order of magnitude and small constant. Consequently, the performance impact is approximately 780x for obtaining stack traces. Finally, if you also need to identify the filename, line number, and column number (fNeedFileInfo = true), this is 50% slower again. In NET 2.0, GetStackTrace was considerably faster by orders of magnitude. See this link. Dynamic method name identification is often used in WPF to automate the implementation of INotifyPropertyChanged, eliminating magic strings, or in remote servers for logging. Note that in the former case, the property setters must be decorated with [MethodImplAttribute(MethodImplOptions.NoInlining)] or the stack trace analysis can fail in Release builds. The following numbers illustrate the impact of current method identification on code performance. The test was to change the values of 3 properties on a single object 100,000 times. The property data types were double, int, and string; the new values were constants; and each setter was decorated with NoInlining . Each property setter also called RaisePropertyChanged, to make this code as similar to real code as possible. This was performed on NET 4.0 using a Debug build on a quad-core Intel i7 950 (hyperthreaded).
If you need higher performance, RtlCaptureStackBackTrace may provide a solution. According to this MSDN forum posting, the method provides very fast performance in debug builds for information on the current call stack only. For more information on automatic implementations of WPF's INotifyPropertyChanged interface, see this blog posting. This appears to be the most comprehensive writeup on this subject to date. The fastest ways to identify the current method for IPropertyChanged notification setters are:
Note that in the vast majority of cases, the overhead of StackTrace methods is irrelevant to the application. It does add 0.12 ms to a property setter call, but unless the code is setting more than, say, 100 properties per second, the overhead is irrelevant. Q: How do I add a member to an Expando object?A: Cast it to an IDictionary<string, object> and use that. Q: In C#, what is the performance penalty of making a method call via a delegate compared to MethodBase.Invoke?A: MethodBase.Invoke is significantly slower at around 40 times slower. This is a constant slowdown, not a first-time-run impact, as is common with reflection. Q: My AssemblyResolve handler fails with a StackOverflow. How can I load the assembly if I can't call System.Reflection.Assembly.Load from this handler?A: Use a different API. AssemblyResolve is not like old-style Win32 APIs - it is only called for assemblies that could not be resolved, not for all assemblies. Consequently, there is no need for the old-style logic of if (!Assembly.Load()) { ...retry intelligently...}, since this handler only receives calls for assemblies that NET could not load. This means the handler must ultimately produce a filename. All Assembly.Load* calls that are not recursive use a filename, either directly or indirectly. These are LoadFile, Load(byte[]), and LoadFrom. Q: What's the fastest way to run a dynamically compiled method?A: Use a delegate to fix the call signature, then call via the delegate. Do not use MethodBase.Invoke. This looses the flexibility of a dynamic method signature in exchange for performance. To mitigate the impact of this, implement and use the dynamically generated class with a dynamic object: derive the dynamically generated class from System.Dynamic.ExpandoObject, and store/use it in a dynamic variable. For most cases, however, MethodBase.Invoke is quite fast enough. Q: In C#, how do I define a class that is only accessible within a specific namespace?A: You can't - the C# language specification does not support this. The closest you can come is to declare the class internal, which allows any class in that assembly to access it. Q: In C#, how do I define a semi-private class, one where the constructor is only accessible to a parent class so no other classes can create instances?A: There are only two ways of accomplishing this: 1. Nested Class a. Declare an interface for all publicly-accessible methods/properties of the semi-private class. b. Define the semi-private class as a private nested class with a public constructor. c. Create and return instances in the containing class as needed. 2. Static Factory a. Declare the semi-private class with a private constructor. b. Implement static constructor methods to construct instances of the class or add them to exiting objects. Q: How can I eliminate the speed penalties of reflection member access/invocation?A: Use fixed-type delegates. The reflection API is slow due to the typeless nature of the APIs. See the FasterFlect project - it reduces member access/invocation penalties from 200x times to 1.5 average, 2.0 maximum. Q: How can I manipulate Generic parameters in a type specific way, such as partial template specialization in C++?A: There is no direct way to do this, but there are a couple workarounds. In all cases, you must check the type of the generic parameter and handle it by-type individually, but this is the intention of partial template specialization anyway. Note that none of the workarounds has zero performance impact as with C++ partial template specialization. 1. Cast to specific type through a cast to object. For example, say you need to store a value in one of several variable arrays depending on the type of the variable. After detecting the specific type, you turn the generic parameter into a concrete type by casting it to object, then to the concrete type. This takes advantage of C#'s autoboxing and unboxing capabilities. This imparts a performance penalty of 8x the time of a strongly typed function. public void SetVariableValueOldOk<Tvalue>( int variableStorageElementNo, Tvalue newValue ) { if (typeof(Tvalue) == typeof(double)) variableStorageDouble[variableStorageElementNo] = (double) (object) newValue; else if (typeof(Tvalue) == typeof(int)) variableStorageInt[variableStorageElementNo] = (int) (object) newValue; // Handle other types ... }
public unsafe void SetVariableValue<Tvalue>( int variableStorageElementNo, Tvalue newValue ) { IntPtr valuePointer; double* valuePointerDouble; int* valuePointerInt; System.Runtime.InteropServices.GCHandle genericObjectHandle; genericObjectHandle = System.Runtime.InteropServices.GCHandle.Alloc(newValue, System.Runtime.InteropServices.GCHandleType.Pinned); valuePointer = genericObjectHandle.AddrOfPinnedObject(); if (typeof(Tvalue) == typeof(double)) { valuePointerDouble = (double*) valuePointer; variableStorageDouble[variableStorageElementNo] = *valuePointerDouble; } else if (typeof(Tvalue) == typeof(int)) { valuePointerInt = (int*) valuePointer; variableStorageInt[variableStorageElementNo] = *valuePointerInt; } genericObjectHandle.Free(); } Q: How do I call a NET object in another process, like using a COM out-of-proc (LocalServer) server?A: Use one of the following options. There is no direct object invocation as in COM. 1. Implement a client/server system using .NET Remoting. Note this supports only Tcp and Http channels, although you can roll your own. 2. Implement a client/server system using System.ServiceModel.ServiceHost. This also supports NetNamedPipeBinding and a couple others. 3. Implement a WCF service in the other process. This is ultimately the same as .NET remoting. 4. Write some custom IPC mechanism. 5. If you launched the other process via a CreateDomain call, you can use a cross-domain delegate. See http://msdn.microsoft.com/en-us/library/ms173139%28VS.80%29.aspx for more information. Q: In C#, how do I create an instance of an internal class from another assembly?A: Do the following: 1. Get an Assembly object for the assembly where the class is defined. 2. Call the assembly's GetType method to get a type object. For example, xmlAssembly.GetType("System.Xml.XsdValidatingReader") 3. Use GetConstructor to get the constructor method, specifying the argument types. GetConstructors will not return a list of all constructors in this usage - you must know what you are looking for. 4. Call the constructor method's Invoke. The constructed object is the return value. See also the AccessPrivateWrapper, shown here. A: Most services only need to know about startup and shutdown. This can be handled with code in the constructor and Dispose. For more than this, implement a custom ServiceHostFactory and add a Factory= attribute to the SVC file equal to the full name of the factory class. For more information, see msdn. Q: When using the UIAutomation Framework, I get a NonComvisibleBaseClass exception. How do I fix this?A: This is actually a normal exception that can be ignored, but by default Visual Studio's exception assistant breaks execution. The fix is to disable automatic break when this exception is thrown by 1. Navigate to Debug->Exceptions... 2. Expand "Managed Debugging Assistants" 3. Uncheck the NonComVisibleBaseClass Thrown option. 4. Click [Ok] Courtesy this MSDN discussion. Q: If I call a property or method on a .NET UIAutomation Framework control object that is no longer visible, it hangs for 60 seconds, then throws. How do I detect this or somehow avoid the 60 second delay?A: No way known. Use the COM API instead, it simply fails immediately, and since it is COM, no exception is thrown. Q: If I call the UIAutomation COM AddPropertyChangedEventHandler for UIA_IsEnabledPropertyId on a button on a Windows Forms GUI, I never get a callback, but I do for WPF GUIs. How do I fix this?A: Unknown. Inspector shows all properties as unsupported and no property in the enum appeared to work. However, window interactive state changes were returned. Switch to polling instead. |