What is the purpose of GC.KeepAlive(object obj)? From the method name, it sounds like it will keep obj from being garbage collected after the call. In fact, the opposite is true, it will keep obj from being garbage collected before the call. On the face, that sounds like a pretty absurd method, doesn't it.
The problem is that the method is named incorrectly. Better, but more verbose, names would be GC.MakeExplicitlyElligibleForGarbageCollection() or GC.DontCollectObjectBeforeNow().
So, what's really going on here? As you are presumably well aware, memory is reclaimed in the .NET framework through garbage collection, which can happen at any time, for any object that has no outstanding references (but don't confuse with reference counting). For example:
public void SomeMethod()
{
SomeClass o = new SomeClass();
GC.Collect(); // no references to 0, it may be collected
return;
}
Not very exciting, and makes sense, right? o isn't being used after the point of instantiation, so the collector is free to clean it up and reclaim the memory*.
But suppose that for some reason or another you need o to remain 'alive' until after SomeMethod() exits. In order to guarantee that, you would need to add some reference to 0 at the end of the method so that it can't be collected. That is the purpose of GC.KeepAlive().
So, all that GC.KeepAlive() does is create an artificial reference to 0 so that it can't be collected prior to the GC.KeepAlive() call:
public void SomeMethod_V2()
{
SomeClass o = new SomeClass();
GC.Collect(); // reference to 0 below, it will NOT be collected
GC.KeepAlive(o);
GC.Collect(); // no more references to 0, it may now be collected
return;
}
So, hopefully the above makes it clearer what GC.KeepAlive() does, although we do need to figure out a better name...
*Note: In order to facilitate debuggers, when an application is compiled in Debug mode, the JIT will NOT collect locally-allocated instances until after the method exits. So, in this example, if compiled under Debug, o will never be collected until after SomeMethod() returns. In Release mode, locally-allocated instances can be collected after the point of last reference, even before the method has returned.
Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts
Friday, October 16, 2009
Tuesday, December 16, 2008
Get Method or Property Name as String in .Net
There are a few occasions where it is helpful to have the string name of the executing method or property.
The cleanest way that I've been able to devise is to use the query the current stack frame using System.Diagnostics.StackFrame.
string methodName = new StackFrame().GetMethod().Name;
When called from within a property, the name includes the getter/setter prefix: get_ or set_. Therefore, if you want the actual property name, use the following:
string propertyName = new StackFrame().GetMethod().Name.Split('_')[1];
The cleanest way that I've been able to devise is to use the query the current stack frame using System.Diagnostics.StackFrame.
string methodName = new StackFrame().GetMethod().Name;
When called from within a property, the name includes the getter/setter prefix: get_ or set_. Therefore, if you want the actual property name, use the following:
string propertyName = new StackFrame().GetMethod().Name.Split('_')[1];
Monday, November 24, 2008
Directory.Delete and Read-only Files
The System.IO.Directory.Delete() method will fail (exception) when it encounters a read-only file, regardless of the recursive flag. Unfortunately, the designers of this method did not have the foresight to provide a means around this limitation.
First off, the exception (UnauthorizedAccessException) only contains the file name; no path information is given. So, if you are in the middle of a recursive delete, the file name is largely irrelevant and there isn't much (programatically) that can be done to manage the situation. The appropriate implementation would have been to include the full path & file name information in the exception, and let the implementor decide on how to represent the file to the UI, as needed.
Second, there is no provision to handle read-only or similar situations w/ the method. At a minimum, there should be the capability to specify whether or not a read-only file should be deleted or not.
Because of this, the recursive implementation of Directory.Delete() is largely useless as it doesn't do what it has been designed to do: delete a directory (and containing files). The consequence of this is that we must write our own method to perform what is actually wanted -- hardly efficient. Anyway, to that end, here is a small function that implements more complete and usable delete functionality:
public void DirectoryDelete(string path, bool recursive)
{
if (recursive)
foreach (string directory in Directory.GetDirectories(path))
DirectoryDelete(directory, true);
foreach (string file in Directory.GetFiles(path))
File.SetAttributes(file, FileAttributes.Normal);
Directory.Delete(path, true);
}
First off, the exception (UnauthorizedAccessException) only contains the file name; no path information is given. So, if you are in the middle of a recursive delete, the file name is largely irrelevant and there isn't much (programatically) that can be done to manage the situation. The appropriate implementation would have been to include the full path & file name information in the exception, and let the implementor decide on how to represent the file to the UI, as needed.
Second, there is no provision to handle read-only or similar situations w/ the method. At a minimum, there should be the capability to specify whether or not a read-only file should be deleted or not.
Because of this, the recursive implementation of Directory.Delete() is largely useless as it doesn't do what it has been designed to do: delete a directory (and containing files). The consequence of this is that we must write our own method to perform what is actually wanted -- hardly efficient. Anyway, to that end, here is a small function that implements more complete and usable delete functionality:
public void DirectoryDelete(string path, bool recursive)
{
if (recursive)
foreach (string directory in Directory.GetDirectories(path))
DirectoryDelete(directory, true);
foreach (string file in Directory.GetFiles(path))
File.SetAttributes(file, FileAttributes.Normal);
Directory.Delete(path, true);
}
Friday, September 19, 2008
Build Warning: The file 'filename.ext' could not be added to the project...
I recently encountered a build warning (C# project, Visual Studio 2005) that was less than informative and somewhat difficult to resolve:
Warning: The file 'keyfile.snk' could not be added to the project. A file with the same path already exists in the project. MyProject.UI
(Note: keyfile.snk and MyProject.UI can be any file 0r project.)
Other than the terse warning, there was nothing to go on. I tried deleting the file from the OS, removing/re-adding to the project, turning off signing, etc. Nothing worked, the error persisted.
I finally opened up the offending project file (MyProject.UI.csproj in this case) in Notepad, and searched for this particular file (keyfile.snk). The source of the warning became immediately obvious -- within one of the <ItemGroup>blocks, I noticed the following:
...
<None Include="keyfile.snk" />
<None Include="keyfile.snk" />
...
So, even though the file was only being included/referenced/visible once in the solution explorer, it was actually being included twice in the project file. I have no idea how/why that happened.
Regardless, the solution is simple -- remove one of the lines, save, and reload your project.
Warning: The file 'keyfile.snk' could not be added to the project. A file with the same path already exists in the project. MyProject.UI
(Note: keyfile.snk and MyProject.UI can be any file 0r project.)
Other than the terse warning, there was nothing to go on. I tried deleting the file from the OS, removing/re-adding to the project, turning off signing, etc. Nothing worked, the error persisted.
I finally opened up the offending project file (MyProject.UI.csproj in this case) in Notepad, and searched for this particular file (keyfile.snk). The source of the warning became immediately obvious -- within one of the <ItemGroup>
...
<None Include="keyfile.snk" />
...
So, even though the file was only being included/referenced/visible once in the solution explorer, it was actually being included twice in the project file. I have no idea how/why that happened.
Regardless, the solution is simple -- remove one of the lines, save, and reload your project.
Monday, August 18, 2008
Dynamically Changing Cultures and String Resources
I'm working on a project that allows the user to dynamically specify the language used in the UI. This can be set independent of the operating system language settings, and is immediately applied.
In our case, most of our strings are retrieved from resources using ResXFileCodeGeneratorEx tool-generated code. When the user switches the language, we simply set the application CurrentCulture and CurrentUICulture, and refresh the display.
However, I currently ran into an issue where a dialog box wasn't displaying text in the alternate languages. The localized text was in the resource, and everything should have been working as expected.
After a little digging, I realized that the dialog was being displayed in the context of another thread. I would have expected that the .Net framework thread initialization code would use the culture of the executing thread, but rather it defaults to the current OS culture. Subsequently, attempts to retrieve a localized string on that new thread would return text in current OS culture (English) -- not what was anticipated.
The solution was to simply set the Thread.CurrentUICulture (and Thread.CurrentCulture) properties to the values from the executing (main) thread:
thread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
thread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
In our case, most of our strings are retrieved from resources using ResXFileCodeGeneratorEx tool-generated code. When the user switches the language, we simply set the application CurrentCulture and CurrentUICulture, and refresh the display.
However, I currently ran into an issue where a dialog box wasn't displaying text in the alternate languages. The localized text was in the resource, and everything should have been working as expected.
After a little digging, I realized that the dialog was being displayed in the context of another thread. I would have expected that the .Net framework thread initialization code would use the culture of the executing thread, but rather it defaults to the current OS culture. Subsequently, attempts to retrieve a localized string on that new thread would return text in current OS culture (English) -- not what was anticipated.
The solution was to simply set the Thread.CurrentUICulture (and Thread.CurrentCulture) properties to the values from the executing (main) thread:
thread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture;
thread.CurrentCulture = System.Threading.Thread.CurrentThread.CurrentCulture;
Thursday, August 14, 2008
Failed string.Format w/ Parameters using CJK
I recently ran into an issue in a localized application that was using the C# .Net Framework string.Format("blah-blah {0}", parm1).
In English, everything worked fine and as expected. However when using a localized format string (Japanese), the parameter substitution failed:
string.Format("数字は、{0}", parm1);
(If you don't have the necessary language pack installed, here is a image representation of the text:
)
It turns out that the translated text was using different braces than 0x7B and 0x7D. In the translated text the left brace was 0xFF5B. Looks like a brace, but isn't for the purposes of string.Format. Further, in looking at the MS Mincho char set, I couldn't find that particular brace. Using the Alt+65371 trick for that character in notepad, I'd get a left bracket -- don't know what/where the brace comes from...
The simple fix was to change the 0xFF5B brace to the correct 0x7B, and then string.Format worked as expected.
In English, everything worked fine and as expected. However when using a localized format string (Japanese), the parameter substitution failed:
string.Format("数字は、{0}", parm1);
(If you don't have the necessary language pack installed, here is a image representation of the text:

It turns out that the translated text was using different braces than 0x7B and 0x7D. In the translated text the left brace was 0xFF5B. Looks like a brace, but isn't for the purposes of string.Format. Further, in looking at the MS Mincho char set, I couldn't find that particular brace. Using the Alt+65371 trick for that character in notepad, I'd get a left bracket -- don't know what/where the brace comes from...
The simple fix was to change the 0xFF5B brace to the correct 0x7B, and then string.Format worked as expected.
Subscribe to:
Posts (Atom)