Last week, I stumbled upon the following piece of code:

 

IList<double> doubles = new List<double> {22.123, 400.12, 100.22};
foreach (int number in doubles)
{
    Console.WriteLine("The Current Number Is : {0}", number);
}

Can you see the bug in that code? We convert all of the doubles in the list to integers, truncating them. I was very surprised that the compiler didn’t warn me about this issue, especially because the compiler would not let me write this code:

 

double someDouble = 22.523;
int someInt = someDouble;

An explicit conversion is necessary here:

 

int someInt = (int)someDouble;

What is the difference between those two cases? Can’t I expect a uniform behavior from the compiler? The reason is probably historic – before C# 2.0 which introduced us with Generics, all of the containers held their items as objects. If the foreach statement didn’t automatically do the conversion, the user would have to explicitly cast every item in the collection from object

 

 

ArrayList array = new ArrayList {"moshe", "levi"};
foreach (object obj in array)
{
    string str = obj as string;
    if (str != null)
       Console.WriteLine("The Current element is {0}", str);
}

In most cases, the collection items are of the same type so in order to reduce the explicit conversion effort, the C# developers decided to do an automatic conversion for us. If we test the MSIL which is generated for the foreach statement, we can clearly notice to the “injected” code which is responsible for the conversion:

 

clip_image002

Now that we understand what happens, here is a way to avoid those kinds of bugs. Using the var keyword, we can ask the compiler to define the type of the collection item according to its value. Here it is:

IList<double> doubles = new List<double> {22.123, 400.12, 100.22};
foreach (var number in doubles)
{
    Console.WriteLine("The Current Number Is : {0}", number);
}

By changing this code, the compiler is responsible for determining the type of “number”. In most cases, that is exactly what we want…

Tags :

7 Responses to “Pay Attention to the Foreach Implicit Casting”


  1. Frank Quednau

    Said on July 28, 2009 :

    In cases where some type provides you only with a non-generic IEnumerable you will be provided with object as the var type. THat may not be what you want. The implicit casting is a remnant of 1.1 days, I don’t think it would be too necessary right now, alas, the non-generic interfaces are still around.

  2. Frank Quednau

    Said on July 28, 2009 :

    Forget my comment…

  3. Oisin G.

    Said on July 29, 2009 :

    bwt, when you say “an implicit conversion is needed here”, you really mean an explicit one…

  4. Josh

    Said on August 4, 2009 :

    Sob, I didn’t see the problem. You’ve really taken a solid look at the whole casting situation here. Thx for making your results available.

  5. Arun

    Said on December 29, 2010 :

    Im using this code
    when i m search the MailItem if it got the Mailmeeting and calender then it break the code not read the mailitem please help me
    Outlook.Application oOutlook;
    Outlook.NameSpace oNs;
    Outlook.MAPIFolder oFldr;
    long iAttachCnt;
    oOutlook = new Outlook.Application();
    oNs = oOutlook.GetNamespace(“MAPI”);
    oFldr = oNs.PickFolder();
    foreach (Outlook.MailItem oMessage in oFldr.Items)
    {
    iAttachCnt = oMessage.Attachments.Count;
    if (iAttachCnt > 0)
    {
    for (int i = 1; i <= iAttachCnt; i++)
    {
    string FileNameExt = oMessage.Attachments[i].FileName;
    FileNameExt = Path.GetExtension(FileNameExt);
    if (FileNameExt == “.docx” || FileNameExt == “.doc”)
    {
    StringBuilder str = new StringBuilder();
    StringBuilder strBody = new StringBuilder();
    str.Append(oMessage.SenderName.ToString() + “,”);
    if (oMessage.Subject != null)
    {
    str.Append(oMessage.Subject.ToString() + “,”);
    }
    else
    {
    str.Append(string.Empty + “,”);
    }
    str.Append(oMessage.SentOn.ToShortDateString() + “,”);
    try
    {
    if (oMessage.Body != null)
    {
    strBody.Append(oMessage.Body.ToString());
    }
    else
    {
    strBody.Append(string.Empty);
    }
    }
    catch { }
    str.Append(oMessage.SenderEmailAddress.ToString() + “,”);
    str.Append(i.ToString() + “,” + oMessage.Attachments[i].FileName + “,”);
    str.Append(oMessage.To.ToString() + “,”);

    string TextString = str.ToString();
    string[] NewText = TextString.Split(‘,’);
    {
    string SenderName = NewText[0].ToString();
    string Subject = NewText[1].ToString();
    string SentOn = NewText[2].ToString();
    string Body = strBody.ToString();
    string SenderEmailId = NewText[3].ToString();
    string Attatchment = NewText[5].ToString();
    string Tomail =NewText[6].ToString();
    SaveInformation(SenderName, Subject, SentOn, Body, SenderEmailId, Attatchment,Tomail);
    }
    string AttatchmentSave = oMessage.Attachments[i].FileName;
    oMessage.Attachments[i].SaveAsFile(“C:\\Users\\abc\\Desktop\\Attatchment\\” + AttatchmentSave);
    }
    else
    {

    }
    }
    }
    }

  6. Moshe

    Said on January 3, 2011 :

    Arun,

    Sorry for being rude but I must say it:

    A. What the hell does it have to do with the subject of this blog post? the fact that your code contains the foreach keyword? when you want real help you must try harder then pushing the “I’m feeling lucky” button in Google.

    B. Without proper
    Indentation, your
    code is
    practically
    Useless.

    C. Here is my advise: After you find why your code is broken find the time to read Clean Code by Uncle Bob. Some of the gems inside will change your life.

    Have a nice day.
    Moshe.

1 Trackback(s)

  1. Jul 29, 2009: Reflective Perspective - Chris Alcock » The Morning Brew #400

Post a Comment