In light of this question I thought it'd be really neat to have a similar question about C#/.Net.
So, what is the most awkward or misleading method name of the .Net and/or C# API?
In light of this question I thought it'd be really neat to have a similar question about C#/.Net.
So, what is the most awkward or misleading method name of the .Net and/or C# API?
I think all the DateTime
methods starting with Add
are misleading, considering DateTime
is an immutable struct and cannot be changed (these methods return values reflecting the addition).
So:
DateTime.Add
DateTime.AddTicks
DateTime.AddMilliseconds
etc.
I would've gone with:
DateTime.Plus
DateTime.PlusTicks
DateTime.PlusMilliseconds
etc.
Arguably the same is true of certain string
methods, such as string.Replace
; but I don't really know what else you would call that.
Some have raised the objection that since the behavior of the addition operator (+
) for numerical values is intuitive (and does not modify the value of the instance), the DateTime.Add
(*) methods are no less intuitive. Here's my response to that.
Let's phrase this objection as:
You don't expect that
i + 5
modifiesi
, so why would you thinkd.Add(t)
is any different?
Now let's repeat this exact same question, but with English words in place of code:
You don't expect that i plus 5 modifies i, so why would you think d add t is any different?
See what I'm saying?
Wow, if you want to see one of the longest comment threads ever on an SO question (that I've seen, anyway), just get a load of the comments to this answer. Apparently the notion that the word Add
suggests mutability is far more subjective than I thought.
One answer: IList<T>.IsReadOnly
vs IList.IsReadOnly
. The meaning is not clear at all, and it turns out these two properties have different meanings. :)
The blog post I created is summarized here:
There are two types of updates:
For IList
The value of IsReadOnly is false if either type of update is allowed. It is only set to true if both types of updates are not allowed.
but for IList<T>
[IsReadOnly] should be true if either type of update is not allowed. It is only set to false if both types of updates are allowed.
Here's a classic: Path.GetInvalidPathChars, which "is not guaranteed to contain the complete set of characters that are invalid"
Another one that has tripped up many innocent coders: Socket.Connected, which is next to useless. It "reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection [complex code horror removed]".
It's not a method, but protected internal
has claimed many victims.
Many people (cough cough) initially expect it to mean that only derived types in the current assembly can access the member, but it actually permits derived types in any assembly and any type in the current assembly to access the member.
Eric Lippert explains why this is eminently reasonable in his blog post here (thank you Brian for the link!). It's just a shame it's ambiguous.
Not quite a method name, but I think the behavior of the ValidationExpression property in a RegularExpressionValidator is misleading. A regular expression normally does a partial match unless you explicitly use the start/end anchors (^ and $). The validator always does a full match, with or without the anchors. If you want a "partial" match, you actually have to explicitly match any characters. So you have the following "conversion":
The Reverse
extension method is strangely named, in my opinion. Why? Because List<T>.Reverse
already exists, and it reverses the ordering of the list itself. The Reverse
extension method, on the other hand, just enumerates over any IEnumerable<T>
backwards.
I would've called it Backwards
, maybe (or even Reversed
, with a "d"). That wouldn't have implied performing any alteration on the list. Plus that way if I wanted to enumerate over a List<T>
backwards I could call:
myList.Backwards()
instead of:
((IEnumerable<T>)myList).Reverse()
or:
myList.AsEnumerable().Reverse()
I'm fond of CompilerParameters.GenerateInMemory (System.CodeDom.Compiler), which doesn't, at least with the C# CodeProvider
Another "oldie but a goodie": ICollection.IsSynchronized; its documentation defines a "thread-safe collection" as one that is not thread-safe during enumeration.
This might seem like a weird answer, but the first time I learned that ToString
on an enum actually returns that enum's name, I was pretty shocked.
To me, this was as surprising as if the following code:
const int SomeSpecialConstant = 1318942;
Console.WriteLine(SomeSpecialConstant.ToString());
Had produced this output:
SomeSpecialConstant
!
In the end, Enum.ToString
is extremely convenient, but I still find myself scratching my head about it from time to time.
ASP.NET developers should recognize this one:
HttpResponse.IsClientConnected
This method only works when running the web app in IIS, not in the development web server.
I find it odd that a generic List has the ForEach extension method and generic IEnumerable does not. Especially since IEnumerable exists to support the foreach() statement.
var myList = new List<SomeClass>();
myList.ForEach(x => Console.Write(x.SomeProp)); // works
IEnumerable<SomeClass> myEnumerable = myList;
myEnumerable.ForEach(); <--- What? Where is it? Invalid
Just saw this nugget
Not every Freezable object can be frozen. To avoid throwing an InvalidOperationException, check the value of the Freezable object's CanFreeze property to determine whether it can be frozen before attempting to freeze it.
from MSDN
I found people coming from C++ backgrounds have really tough time visualising that structs instances created using new dont go on the heap!!
The mechanism is hidden deep in CLR - When new is used with reference types it is converted to "newobj" CIL instruction and for value types it is converted to "initobj"
Edit: This makes a perfect interview trick question
Microsoft.VisualBasic.Strings.Trim() doesn't always return the same value as "string".Trim()
The VB method will only remove actual space characters(from hitting the space key), while the other function will remove all the other white space characters(carriage returns, etc) with it.
My vote goes to the ICloneable
interface and its Clone method.
The term is so vague and ambiguous that important people at Microsoft actually tell you not to implement it or use it in public APIs.
Not only does it tell you nothing about what a "clone" is (is it a memberwise clone? Deep copy? How deep? What if it has external references? What if it references a singleton?) but it's entirely possible for this method to return a different type from the original, since the return type is just System.Object
.
It's a thoroughly pointless interface that provides no discoverability whatsoever, and yet it's there, ever present, leading another programmer astray every week. Look at the ICloneable questions and see for yourself. Endless confusion.
I find the 'TryParse' methods to be awkward, and somewhat misleading.
int i = 15;
var b = int.TryParse("what", out i);
// now i is 0 and b is false
Maybe this is just a minor complaint, and I understand the decisions that went into that API, but it's not entirely clear that i becomes 0, and having to use an 'out' parameter can be a real pain, especially if you don't care what the resultant value is (only that it is valid).
Select()
When MS created LINQ, they used the nomenclature of SQL. So Select, Where, OrderBy, etc. The problem is that these functions already exist in other languages, but with different names:
This is really confusing.
Just discovered yet another answer. If you take two types FromType
and ToType
, with a conversion from FromType
to ToType
, then foreach
has an interesting behavior:
foreach (ToType entry in collection)
works beautifully when collection
has a known element type (e.g., List<FromType>
). However, it fails with an InvalidCastException
if collection
doesn't know its element type (e.g., IEnumerable
).
This little gem is courtesy of Bill Wagner; see Item 3 in Effective C#. The behavior is a holdover from the pre-generics .NET days...
Full repro code:
using System;
using System.Collections;
using System.Collections.Generic;
class Program
{
public sealed class FromType
{
}
public sealed class ToType
{
public static explicit operator ToType(FromType from)
{
return new ToType();
}
}
static void Main(string[] args)
{
var test1 = new List<FromType>() { new FromType() };
try
{
foreach (ToType entry in test1)
{
Console.WriteLine("Cast OK.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
IEnumerable test2 = test1;
try
{
foreach (ToType entry in test2)
{
Console.WriteLine("Cast OK.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
}
public Form1()
{
InitializeComponent();
Control.ControlCollection controls1 = this.Controls;
controls1.Add(new Button());
controls1.Add(new Button());
ControlCollection controls2 = new ControlCollection(new Form());
controls2.Add(new Button());
controls2.Add(new Button());
ControlCollection controls3 = new ControlCollection(new Form());
foreach (Control item in controls1)
{
controls3.Add(item);
}
foreach (Control item in controls2)
{
controls3.Add(item);
}
}
After execution:
controls1.Count = 1
controls2.Count = 2
controls3.Count = 3
Now replace:
Control.ControlCollection controls1 = this.Controls;
with:
ControlCollection controls1 = new ControlCollection(this);
After execution:
controls1.Count = 2
controls2.Count = 2
controls3.Count = 4
Lesson?
System.Windows.Forms.Form.ControlCollection !=
System.Windows.Forms.Control.ControlCollection
String.IsNullOrEmpty(" ") returns false. Once could argue or easily believe it should return true.
Something like String.IsNullOrEmptyOrWhitespaceOnly(string) would be nifty, if the name could be shorter.