 |
|
 |
I've expanded this forum to include not just subtle bugs, but any small code snippets that showcase truly excellent code. Something complicated, elegant, subtle - something that got you the hot date with the cute developer down the hall. Anything that you think others will enjoy and appreciate, like a complex wine.
cheers, Chris Maunder
The Code Project Co-founder Microsoft C++ MVP
|
| Sign In·View Thread·PermaLink | 4.52/5 (11 votes) |
|
|
|
 |
|
 |
Writing a generic collection and want to dispose of items that implement IDisposable, but don't want to bother checking each item or type for IDisposable?
Why not associate a bogus Dispose method with every class that doesn't already have one?
public static void Dispose ( this object Object ){}

(Just be careful of non-IDisposable classes that have a Dispose method anyway. )
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The only problem though with doing that is that you'll never reach the actual dispose method, unless you've reference it as the type that defines the dispose method. But then, if you're going to do that, you wouldn't need the above extension.
Why not just create an extension method that does the following:
public static void Dispose(this object Object) { IDisposable disposable;
if ((disposable = (Object as IDisposable)) != null) disposable.Dispose(); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Actually, it would. The generic overloading rules I assume you are basing your position on only apply at source compile time. Since the actual type is not known at the time your generic collection (or whatever) is compiled, you would end up always calling the extension method. A simple example to demonstrate:
Module Module1
Sub Main() Dim di As New Disposer(Of Integer)(43) Dim dd As New Disposer(Of DisposableTest)(New DisposableTest)
Console.WriteLine("Disposing Disposer(Of Integer)") di.Cleanup() Console.WriteLine("Disposing Disposer(Of DisposableTest)") dd.Cleanup()
Dim dtemp As New DisposableTest() Console.WriteLine("Disposing DisposableTest") dtemp.Dispose()
Console.WriteLine("Program complete. Press any key to exit...") Console.ReadKey() End Sub End Module
Public Module HelperExtensions <System.Runtime.CompilerServices.Extension()> _ Public Sub Dispose(ByVal this As Object) Console.WriteLine("Extension dispose") End Sub End Module
Public Class Disposer(Of T) Public Sub New(ByVal data As T) _data = data End Sub
Private _data As T
Public Sub Cleanup() If _data IsNot Nothing Then _data.Dispose() End Sub End Class
Public Class DisposableTest Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose Console.WriteLine("Class dispose called.") End Sub End Class
If you run that, the output will show that the extension dispose is being called for both Disposers even though the data object for one of them is IDisposable.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hmmm... you're right, I didn't test it. I had originally written it with the test for IDisposable then removed it thinking it unnecessary. Ah well, it's not a serious piece of code anyway.
On the other hand, I don't think it's supposed to work this way.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
PIEBALDconsult wrote: On the other hand, I don't think it's supposed to work this way.
Extension methods are, from what I understand, an ugly kludge which serves mainly to make Intellisense more useful, at the expense of introducing all sorts of potential weird bugs.
If I had my druthers, compilers would support extension classes rather than extension methods; from a run-time perspective, objects of an extension class would in reality be objects of the underlying class; from a compiler perspective, they would be interchangeable, but the extension classes would support extension methods and properties in addition to the methods and properties of the underlying class.
Failing that, the Intellisense usefulness of extension classes would be achieved by making it so that typing "object.extensionMethod(" would be automatically rearranged to "extensionMethod(object," so as to make clear what was actually happening.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
supercat9 wrote: Extension methods are, from what I understand, an ugly kludge
Hear hear! And poorly implemented too. 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
supercat9 wrote: Extension methods are, from what I understand, an ugly kludge which serves mainly to make Intellisense more useful, at the expense of introducing all sorts of potential weird bugs.
It's because, as you so well said, don't understand them!
A train station is where the train stops. A bus station is where the bus stops. On my desk, I have a work station.... _________________________________________________________ My programs never have bugs, they just develop random features.
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
 |
Super Lloyd wrote: It's because, as you so well said, don't understand them!
Suppose I write "foo.bar()". What circumstances will determine whether this:
- Produces a build-time error
- Produces code which will throw "method not implemented" at run-time
- Execute a native method of foo's class
- Execute an extension method (if several exist, which one?)
In the absence of extension methods, if an object is used in two contexts where it is declared identically. foo.bar() will perform the same action in both contexts. It may perform different actions on different objects, and the actions may depend upon how foo is declared, but the objects actual and declared types together serve to completely define the object's public fields, methods, and behaviors.
What precisely determines what code will run when foo.bar() is executed on a system which uses extension methods?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I think it's relatively simple. The declared method of the declared type will be called if it exist. Otherwise it will throw a compile time error unless there is an extension method with such a name for such a declared type in the static classes of the declared using in the referenced DLLs. (I don't know what the "not implemented exception" does there, it will be thrown when whatever method throwing it is called) Further, if you use VS and thanks to intellisense, you'll know right away if: 1. the method exits 2. the method is on the declared type 3. the method is an extension method
none of this should requires much though as: 1. when you use an object and call the method, and assuming you are not producing random code , you know very well what are the methods on the type that you are interested in!
in other word mot only you don't have to think if you use VS, but also, extension method are not ambiguous at all!
heck, me who use extension method a lot, the only problem I found them is that I have to write the "using namespace" explicitly myself, as VS won't do it!
A train station is where the train stops. A bus station is where the bus stops. On my desk, I have a work station.... _________________________________________________________ My programs never have bugs, they just develop random features.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Super Lloyd wrote: The declared method of the declared type will be called if it exist.
That's what I thought until I started this thread.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I think you were thinking the method of the.... runtime type will be called. But there is no ambiguity and no runtime checking, it all happen at compile time! so if you wrote object o; o.foo()
That would fail, because there is no foo() method on object. If it compiles that means that the foo() extension has been detected (at compile) and will be called.
And nothing will change, i.e. if o is not only an object but a subclass which has a foo() method, this method won't be called by surprise contrary to the original extension, the extension will still be called as has been decided at compile time. With extension method there is no runtime snafu to fear, it all happen at compile time and compile time only!
A train station is where the train stops. A bus station is where the bus stops. On my desk, I have a work station.... _________________________________________________________ My programs never have bugs, they just develop random features.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
So are you saying that the extension method will get called if and only if the compiler was unaware of a normally-declared method when the code was built? What happens if between rebuilds a class in a library gets updated and a new property, method, or field gets added which shadows an extension method? My understanding is that the component-oriented architecture was designed to allow methods, properties, and fields to be added to classes without breaking applications that use them (such applications will never attempt to reference the new methods, properties, and fields, and will be unaware of their existence). In the absence of extension methods, if new methods are added to a library but all pre-existing methods, properties, etc. are unchanged, updating the library in an existing project will not affect the project application's behavior. If applications that use a class might have extension methods, how can one safely add classes, methods, or fields to it without potentially breaking applications of which one might be unaware?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It's an interesting scenario that you just outlined, that can indeed happen.
I won't elaborate too much on it (it's left as an exrcise to the reader ), just say that in practice that hasn't happen to me yet. As I created extension method, so far, only for enums, interfaces and the following BCL classes: string, Stream (which haven't changed their signature much), so, in my case, I would say it's pretty safe.
On a late note I would say the "problem" you outlined was on purpose, I think it's used in some of the LINQ libraries.
A train station is where the train stops. A bus station is where the bus stops. On my desk, I have a work station.... _________________________________________________________ My programs never have bugs, they just develop random features.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I use extension methods a lot, and don't face the problems that "could" eventually happen. But, that's because using them a lot and using them in strange manners is different.
For example, let's see the Stream class: Write(buffer, index, count);
But, in general, I call: Write(buffer, 0, buffer.Length);
So, I created an extension method that does this. If, in the future, there is a Write method with only one parameter, I really think it will still do the same I did, so I don't see a problem.
One pair of things I really think should be done with extension methods is "Equals and GetHashCode". I really think these should only exist on objects that implement IEquatable (or IEquatable generic) interface. And, by default, an extension method could check if the object implement thems and, if not, return the ReferenceEquals or the "reference id" of the object. I think that actually it is horrible to know when an object implements equals and when it does not implements. But that's not the case here.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
supercat9 wrote: So are you saying that the extension method will get called if and only if the compiler was unaware of a normally-declared method when the code was built?
Yes, that is correct. Extensions methods are just shorthand - they get converted to the static method call upon compilation, so updating the library with a method name that "conflicts" with the extension name will still call the extension method.
That is, unless you recompile the code that called the extension method before, because it will now point to the instance method 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
supercat9 wrote: ). In the absence of extension methods, if new methods are added to a library but all pre-existing methods, properties, etc. are unchanged, updating the library in an existing project will not affect the project application's behavior. If applications that use a class might have extension methods, how can one safely add classes, methods, or fields to it without potentially breaking applications of which one might be unaware?
Actually, that's not true, unless you don't consider overloading a method to be a new method.
Overload resolution could find a new method to be a better match.
V1 -- class C { void Foo(object o) {} }
V2 --
class C { void Foo(object o) {} void Foo(int x){} }
new C().Foo(1) will now resolve to the int overload.
If Foo was virtual, and you'd derived from C and overridden Foo(object), your code wouldn't get called either.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
PIEBALDconsult wrote: On the other hand, I don't think it's supposed to work this way.
The compiler of course has no run-time type information when generating the code. Hence, it depends on the declared type of a reference to know where to look for members. Since the point of this code was to add a dummy Dispose to types that do not have Dispose, the only possible match is the extension methods.
Personally, I find extension methods to be largely fluff. There is absolutely nothing an extension method can achieve that cannot be achieved with traditional utility methods. Calling class methods using instance syntax is also somewhat confusing, and generally such features tend to be seen as "cool" and misused accordingly. Much of the same can be said of initializers, though they are at least useful for anonymous types (which is a useful addition to the language).
However, the one saving grace of extension methods have to do with a less technical aspect. It can be difficult to keep one's code base so well organized that it's always easy to find (and thus use) one's available utility methods. With extension methods it's possible to have a collection of them in one or any number of classes and discover the relevant ones via intellisense by remembering nothing more than to include a using directive. Some would also say it results in "more readable code", though I think that's rather mixed. string s = textBox1.Text.Reverse(); may be easier to read than string s = StrUtil.Reverse(s);, but since the latter actually shows what happens and the first one does not, it's not necessarily "more readable"!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I recognize that extension methods make Intellisense more useful, but a similar result could have been achieved by allowing methods to be tagged as auto-Intellisense, so that they would be listed in the Intellisense pop-up. Typing "textbox1.Text." might include a reverse function in the pop-up list, but selecting it would replace the period after "text" with a right-paren, and insert "StrUtil.Reverse(" before it (if the method took more parameters, it would use a comma rather than a right-paren.
Actually, one could leverage Intellisense further by using a double-dot notation for extension methods (since double-dot is not otherwise legitimate, Intellisense would know that when a programmer typed "objectname.." he wanted a list of declared applicable methods. For that matter, that might not have been a bad syntax for extension methods. Conceptually, I wouldn't mind extension methods so much if they were distinct from real ones. Having code change meaning on a rebuild, though, seems like a disaster waiting to happen, unless one always performs full rebuilds whenever anything changes.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
While there are times that it would be nice to have Dispose work as a do-nothing for classes that do not implement iDisposable (e.g. I don't see any reason "using" shouldn't work even when it's superfluous), decisions of whether or not to dispose objects should almost always be made on the basis of something other than whether or not the object is of a disposable type. I can imagine considerable utility for a collection which includes a routine to add items and flag them as being both disposable [i]and owned by the collection[/i], so that when the collection is Dispose'd the items will be as well. For best reliability, the item should be passed as a reference parameter and zeroed when the item is added (if an exception is thrown while adding the item, the reference should be null if the item had been added to the list before the exception was thrown, and non-null if it had not).
By the way, I use a global routine called Zap which accepts an iDisposable by reference; if the iDisposable is null, the routine simply exits. Otherwise it calls Dispose on the passed-in object and nulls out the passed-in object reference.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
supercat9 wrote: I don't see any reason "using" shouldn't work even when it's superfluous
Indeed. My feeling is that either object should implement IDisposable ( ) or the using statement shouldn't rely on the IDisposable interface. In fact I dislike language features that rely on the framework.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
PIEBALDconsult wrote: I dislike language features that rely on the framework
would that include "string literals" which get easily created, initialized and interned?
Luc Pattyn
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
Local announcement (Antwerp region): Lange Wapper? 59.24% waren verstandig genoeg om NEEN te stemmen; bye bye viaduct.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
string is a C# keyword, it is required by the language. In the implementation of C# for .net the string type is backed by the System.String type. I (well maybe not me, but someone) could implement the C# language for some other platform (OpenVMS perhaps). Such an implementation may not require that string be backed by System.String, the language doesn't demand it.
In .net, the using statement relies on the existence of an System.IDisposable interface. I feel that this is too tightly coupled. Were I to write my own implementation of C# I would not want to have to have an System.IDisposable interface, the language shouldn't demand it, it's not a keyword.
Were I to write my own implementation of C# I would allow the using statement to operate on any type.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
PIEBALDconsult wrote: may not require that string be backed by System.String
I do want an easy way to initialize and to assign a new literal value to a System.String object. string literals, the thing understood by compilers, does exactly that; so for me a string literal must be a System.String I would not want to write string s=new string(new char[]{'H','e','l','l','o'}; !
PIEBALDconsult wrote: I would allow the using statement to operate on any type
I'm not sure I like the idea; I can see some advantage, but then I don't want code to start looking like this:
using (int i=new int()) using (int j=new int()) for (i=0; i<10; i++) { for (j=0; j<10; j++) { ... } }
PIEBALDconsult wrote: the using statement relies on the existence of an System.IDisposable interface
and what would you do about foreach? it needs an array, or an IEnumerable or an IEnumerable<T>. I got used to it and wouldn't like to loose it. And just ignoring the code block for objects that are not enumerable (without a warning/error) seems inappropriate.
Luc Pattyn
I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages
Local announcement (Antwerp region): Lange Wapper? 59.24% waren verstandig genoeg om NEEN te stemmen; bye bye viaduct.
modified on Monday, October 19, 2009 4:43 PM
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |