locked
Using the Object: "object" RRS feed

  • Question

  • Hi,

    Well I used to program C++ but now I am "in love" with C#, jejeje

    The question is that time to time I need to pass in parameters to a functions that uses as an argument an object. Sometimes I need to pass arrays, lists, dictionaries and other kind of containers, so I cast them to an object and I uncast them to the correct type inside the function. So what I do for example is:

    void FucntionA()
    {
       ...

       List<object> MyArgs = new List<object>();

       MyArgs.Add((object)MyBigDictionaryOfStrings);
       MyArgs.Add((object)MyBigListOfStrings);

       SomeFunction(MyArgs); //<- Expecting an object
    }

    void SomeFunction(object TheArg)
    {
        List<object> MyArgs = (List<object>)TheArg;
        
        Dictionary<string> Dicc = (Dictionary<string>)MyArgs[0];
        List<string> Listt = (List<string>)MyArgs[1];
       
        ...
    }


    Somehow this approach remainds me the usage of "void" in C++. At that time it was not a good practice to use "void" because it was an undefined type, I know C# is different with the garbage collector and so on, but I got a question:

    Is it a good practice to use the object "object" anywhere, anytime, without any worry?

    Thanks,
    Enrique.
     
    Tuesday, December 29, 2009 9:38 PM

Answers


  • Is it a good practice to use the object "object" anywhere, anytime, without any worry?

     

    No, it is not good practice.  There are times when this is unavoidable, and in those cases, you will want to do this.  That's most often the case when you are dealing with a framework API that is trying to work with any type (ie: using ParameterizedThreadStart to start a thread).  In those cases, you'll do this, but you should always be careful when doing so.

    In your example, you know, in advance the type of object - it's always a List<object>.  In this case, it would be much better to just do:

      // ... 
      SomeFunction(MyArgs); //<- Expecting a List<object> directly!
    }
    
    void SomeFunction(List<object> myArgs)
    {
      // Just use myArgs directly now...
    In fact, in most cases, I would actually just pass both the dictionary and list directly, and avoid the List<object> entirely: 
        // Call method directly
        SomeFunction(MyBigListOfStrings, MyBigDictionaryOfStrings);
    }
    
    void SomeFunction(List<string> stringList, Dictionary<string,string> stringDict)
    {
      // Now just use stringList and stringDict as needed
    


    By using the correct types, you're ensuring that the compiler can correctly flag any mistakes you make in your code, which adds a huge amount of safety.  When you work with objects directly, the compiler has no way to tell if your dealing with the correct types.  This makes it much easier to have errors that would otherwise be easily avoided.

    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by Kikeman Tuesday, December 29, 2009 10:14 PM
    • Unmarked as answer by Kikeman Tuesday, December 29, 2009 10:21 PM
    • Marked as answer by Kikeman Tuesday, December 29, 2009 11:29 PM
    Tuesday, December 29, 2009 9:49 PM
  • Yes, in this case, its appropriate to use object.  You are right - this is somewhat like void* in C++, since it's pretty much an untyped "object" that can be used as anything, and all of the type safety loss is the same.

    However, instead of passing a List<object>, I personally would use something that allowed for some type safety.  If you had more than 2 objects, a custom class is probably the best option (until .NET 4, when Tuple is part of the framework).  With two objects, like you have, I would probably rework this to use KeyValuePair, more like:

                // I would just make a KeyValuePair<TKey,TValue>, since it's type safe...
                e.Result = new KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>> (RepsPerDay, RepsODTsInfo2);
            }
    
    
            private void backgroundWorkerDownloadReportsFromAtmoDB_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
    
                    KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>> Results = e.Result as KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>>;
                    // It's good practice to check to make sure this is correct here...
                    if (Results == null)
                        throw new InvalidOperationException("...");
    
                    // These, at least, are now 100% type safe and checked by the compiler
                    SortedDictionary<DateTime, List<AgReport>> LoadedReps = Results.Key;
                    Dictionary<string, double[]> RepsODTsInfo2 = Results.Value;
    

    This still has 1 "object" being used, but only one.  Also, I added a check to make sure the cast is correct (using as, check for null).  This gives you more type-safety from the compiler, and less chances for mistakes to be made.
    Reed Copsey, Jr. - http://reedcopsey.com
    • Edited by Reed Copsey, JrMVP Tuesday, December 29, 2009 10:11 PM typo fix
    • Marked as answer by Kikeman Tuesday, December 29, 2009 10:16 PM
    • Unmarked as answer by Kikeman Tuesday, December 29, 2009 10:21 PM
    • Marked as answer by Kikeman Tuesday, December 29, 2009 11:32 PM
    Tuesday, December 29, 2009 10:11 PM

All replies


  • Is it a good practice to use the object "object" anywhere, anytime, without any worry?

     

    No, it is not good practice.  There are times when this is unavoidable, and in those cases, you will want to do this.  That's most often the case when you are dealing with a framework API that is trying to work with any type (ie: using ParameterizedThreadStart to start a thread).  In those cases, you'll do this, but you should always be careful when doing so.

    In your example, you know, in advance the type of object - it's always a List<object>.  In this case, it would be much better to just do:

      // ... 
      SomeFunction(MyArgs); //<- Expecting a List<object> directly!
    }
    
    void SomeFunction(List<object> myArgs)
    {
      // Just use myArgs directly now...
    In fact, in most cases, I would actually just pass both the dictionary and list directly, and avoid the List<object> entirely: 
        // Call method directly
        SomeFunction(MyBigListOfStrings, MyBigDictionaryOfStrings);
    }
    
    void SomeFunction(List<string> stringList, Dictionary<string,string> stringDict)
    {
      // Now just use stringList and stringDict as needed
    


    By using the correct types, you're ensuring that the compiler can correctly flag any mistakes you make in your code, which adds a huge amount of safety.  When you work with objects directly, the compiler has no way to tell if your dealing with the correct types.  This makes it much easier to have errors that would otherwise be easily avoided.

    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by Kikeman Tuesday, December 29, 2009 10:14 PM
    • Unmarked as answer by Kikeman Tuesday, December 29, 2009 10:21 PM
    • Marked as answer by Kikeman Tuesday, December 29, 2009 11:29 PM
    Tuesday, December 29, 2009 9:49 PM
  • http://msdn.microsoft.com/en-us/library/system.object%28VS.80%29.aspx
    Tuesday, December 29, 2009 9:59 PM
  • So, it looks kind of the same situation with "void" of C++

    Anyway, you are rigth I was pushed to use the first apprach because I am using a BackGroundWorker, in DoWork I am passing as a result "e.Result" some dictionaries and lists that then I use in RunWorkerCompleted():

                List<object> Results = new List<object>();
                Results.Add(RepsPerDay);
                Results.Add(RepsODTsInfo2);
    
                e.Result = Results;
            }
    
    
            private void backgroundWorkerDownloadReportsFromAtmoDB_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
    
                    List<object> Results = (List<object>)e.Result;
                    SortedDictionary<DateTime, List<AgReport>> LoadedReps = (SortedDictionary<DateTime, List<AgReport>>)Results[0];
                    Dictionary<string, double[]> RepsODTsInfo2 = Results[1];
    


    So, using it like here with the BGW DoWork -> RunWorkerCompleted is ok?
    (I dont want to use global data from the BGW)
     

    Tuesday, December 29, 2009 10:00 PM
  • Yes, in this case, its appropriate to use object.  You are right - this is somewhat like void* in C++, since it's pretty much an untyped "object" that can be used as anything, and all of the type safety loss is the same.

    However, instead of passing a List<object>, I personally would use something that allowed for some type safety.  If you had more than 2 objects, a custom class is probably the best option (until .NET 4, when Tuple is part of the framework).  With two objects, like you have, I would probably rework this to use KeyValuePair, more like:

                // I would just make a KeyValuePair<TKey,TValue>, since it's type safe...
                e.Result = new KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>> (RepsPerDay, RepsODTsInfo2);
            }
    
    
            private void backgroundWorkerDownloadReportsFromAtmoDB_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
            {
    
                    KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>> Results = e.Result as KeyValuePair<SortedDictionary<DateTime, List<AgReport>>,Dictionary<string, double[]>>;
                    // It's good practice to check to make sure this is correct here...
                    if (Results == null)
                        throw new InvalidOperationException("...");
    
                    // These, at least, are now 100% type safe and checked by the compiler
                    SortedDictionary<DateTime, List<AgReport>> LoadedReps = Results.Key;
                    Dictionary<string, double[]> RepsODTsInfo2 = Results.Value;
    

    This still has 1 "object" being used, but only one.  Also, I added a check to make sure the cast is correct (using as, check for null).  This gives you more type-safety from the compiler, and less chances for mistakes to be made.
    Reed Copsey, Jr. - http://reedcopsey.com
    • Edited by Reed Copsey, JrMVP Tuesday, December 29, 2009 10:11 PM typo fix
    • Marked as answer by Kikeman Tuesday, December 29, 2009 10:16 PM
    • Unmarked as answer by Kikeman Tuesday, December 29, 2009 10:21 PM
    • Marked as answer by Kikeman Tuesday, December 29, 2009 11:32 PM
    Tuesday, December 29, 2009 10:11 PM
  • I got an error using the "as":

    Error 1 The as operator must be used with a reference type ('System.Collections.Generic.KeyValuePair<System.Collections.Generic.SortedDictionary<System.DateTime,System.Collections.Generic.List<iAgent.AgReport>>,System.Collections.Generic.Dictionary<string,double[]>>' is a value type) C:\Documents and Settings\lze1tl\My Documents\==@ My_WIN_Code\_Aurora_Alpha\ODTReportTime\FrmMainRT.cs 800 23 ODTReportTime

    Should I just cast it?:
    KeyValuePair<SortedDictionary<DateTime, List<AgReport>>, Dictionary<string, double[]>> Results 
    = (KeyValuePair<SortedDictionary<DateTime, List<AgReport>>, Dictionary<string, double[]>>)e.Result;
    
    


    Tuesday, December 29, 2009 10:22 PM
  • Sorry about that - yes.  I forgot that KeyValuePair is a struct.

    Just cast it, and then check the individual values.  If you have a custom class, you can use the as keyword and the approach I took before.


    Reed Copsey, Jr. - http://reedcopsey.com
    Tuesday, December 29, 2009 11:06 PM