locked
Casting with concrete generic type and interfaces RRS feed

  • Question

  • Hi all,

    I've question about casting with a concrete generic type and interfaces. I've a concrete generic class called Foo<T> with implement the IFoo interface and the generic IFoo<T> interface:

    ///

    Generic interface
    public interface IFoo<T> : IFoo
    {
        
    T Value { get; set; }
    }

    /// non generic interface
    public interface IFoo
    {
        
    int Number { get; set; }
    }

    /// Concrete class
    public class Foo<T> : IFoo<T>, IFoo
    {
        // code ...

    public class Bar
    {
        public List<IFoo> Foo;

        public object GetFooValue(int fooNumber)
        {
            IFoo foo = null; // Sorry for the IRubriek type
            // Find foo and parse it
            return foo.Value; // doesn't compile :(
        }
    }

    Is there any way to access the Value property in the GetFooValue method?

    • Edited by Dutchdre Wednesday, February 4, 2009 1:23 PM
    Wednesday, February 4, 2009 12:44 PM

Answers

  • Dutchdre said:

    Are there any performance issues when boxing all the time from object {bool}, object{string} etc to bool and string?



    Yes and no.  Specifically, there are performance considerations when boxing a bool value, but a smaller impact with a string.  The reason for this has to do with the fact that string is already a reference type, but bool is not.  To work around this (at least when referencing the generic version) you could always use a private variable of type T, and return and set that for both the generic and non-generic version of Value.  Even so, you'll still have boxing and unboxing on the set for the Non-Generic version, and you would still have boxing on the return for the non-generic Value, because you'd be boxing an "int" for instance, in order to return the value. Long story short, if you're going to be referencing a non-generic version of a generic parameter, by converting it to object, you're going to do boxing sooner or later.  Many people would disagree with me here, but I personally find the performance impact to be negligable unless you're planning on boxing extremely frequently and are already in a performance-challeneged environment.

    public class Foo<T> : IFoo<T>

    {

        private T _value;

     

        object IFoo.Value

        {

            get { return _value; }

            set { _value = (T)(object)value; }

        }

     

        public T Value

        {

            get

            {

                return _value;

            }

     

            set

            {

                _value = value;

            }

        }

     

        public int Number { get; set; }

    }



    David Morton - http://blog.davemorton.net/
    • Marked as answer by Dutchdre Thursday, February 5, 2009 10:17 AM
    Wednesday, February 4, 2009 10:01 PM

All replies

  • There's no way to tell with the code that you posted.

    You didn't show us the definition of IRubriek, so there's no way for us to know.
    Wednesday, February 4, 2009 1:14 PM
  • Sorry, thats my fault :(

    IRubriek should be IFoo

    • Edited by Dutchdre Wednesday, February 4, 2009 2:34 PM
    Wednesday, February 4, 2009 1:16 PM
  • Ok, so the only way for this to work is (a) to know what the type of T is, and (b) attempt to convert the object to the appropriate IFoo<T>.

    Something like this:

    IFoo foo = ...

    IFoo<string> fooString = foo as Ifoo<string>;

    if (fooString != null)
    {
        string s = fooString.Value;
        ...
        fooString.Value = "test";
    }

    • Proposed as answer by owen-results Friday, May 25, 2018 12:14 AM
    Wednesday, February 4, 2009 2:31 PM
  •  public object GetFooValue(int fooNumber)
        {
            IFoo foo = null; // Sorry for the IRubriek type
            // Find foo and parse it
            return foo.Value; // doesn't compile :(
        }

    IFoo does not contain a member named "Value", it is named "Number".  What does the parameter have to do with this method?

    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, February 4, 2009 3:20 PM
  • Te list contains Foo<string>, Foo<int>, Foo<DateTime> etc. In this example method we use the parameter fooNumber to find the object in the list and do some otherthings with it. But we don't know the generic type of the Foo at design time but we want to access the Value of Foo
    Wednesday, February 4, 2009 3:25 PM
  • Dutchdre said:

    Te list contains Foo<string>, Foo<int>, Foo<DateTime> etc. In this example method we use the parameter fooNumber to find the object in the list and do some otherthings with it. But we don't know the generic type of the Foo at design time but we want to access the Value of Foo



    But, IFoo does not have have a "Value".  IFoo<T> contains a member named Value.

    public class Bar2<T>   
        {  
            public List<IFoo<T>> Foo;  
     
            public T GetFooValue(int fooNumber)  
            {  
                IFoo foo = null// Sorry for the IRubriek type  
                // Find foo and parse it  
                //return foo.Number; // doesn't compile :(  
                return Foo[fooNumber].Value;  
            }  
        } 

    You have not provided enough CLEAR information for an answer.
    Mark the best replies as answers. "Fooling computers since 1971."

    EDIT:  You would objects of type Foo<T> to the above list.  Foo<string>  Foo<int>  Foo<DateTime>
    • Edited by Rudedog2 Wednesday, February 4, 2009 3:52 PM edit
    Wednesday, February 4, 2009 3:40 PM
  • Hi Rudedog2,

    In your code it's not possible to fill the list Foo with instances of Foo<string> and Foo<int> together.
    Here is what i have:

    public class Bar  
    {  
        public List<IFoo> Foo;  
     
        public Bar()  
        {  
            Foo.Add(new Foo<string>() { Number = 1, Value = "Test" });  
            Foo.Add(new Foo<int>() { Number = 2, Value = 100 });  
            Foo.Add(new Foo<DateTime>() { Number = 3, Value = new DateTime(2009, 2, 4) });  
        }  
     
        public object GetFooValue(int fooNumber)  
        {  
            IFoo foo = (IFoo)this.Foo.Find(delegate(IFoo f) { return r.Number == fooNumber; });  
            return foo.Value;  
        }  
    }  
     

    Error:
    'IFoo' does not contain a definition for 'Value' and no extension method 'Value' accepting a first argument of type 'IFoo' could be found (are you missing a using directive or an assembly reference?)


     


     

    Wednesday, February 4, 2009 4:05 PM
  • Rudedog2 said:

     public object GetFooValue(int fooNumber)
        {
            IFoo foo = null; // Sorry for the IRubriek type
            // Find foo and parse it
            return foo.Value; // doesn't compile :(
        }

    IFoo does not contain a member named "Value", it is named "Number".  


    Mark the best replies as answers. "Fooling computers since 1971."



    I thought we knew that already.
    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, February 4, 2009 4:34 PM
  • Dutchdre said:

    Hi Rudedog2,

    In your code it's not possible to fill the list Foo with instances of Foo<string> and Foo<int> together.
    Here is what i have:

    public class Bar  
    {  
        public List<IFoo> Foo;  
     
        public Bar()  
        {  
            Foo.Add(new Foo<string>() { Number = 1, Value = "Test" });  
            Foo.Add(new Foo<int>() { Number = 2, Value = 100 });  
            Foo.Add(new Foo<DateTime>() { Number = 3, Value = new DateTime(2009, 2, 4) });  
        }  
     
        public object GetFooValue(int fooNumber)  
        {  
            IFoo foo = (IFoo)this.Foo.Find(delegate(IFoo f) { return r.Number == fooNumber; });  
            return foo.Value;  
        }  
    }  
     

    Error:
    'IFoo' does not contain a definition for 'Value' and no extension method 'Value' accepting a first argument of type 'IFoo' could be found (are you missing a using directive or an assembly reference?)


     


     



    You have not really asked a question.  No more than, "this does not compile".   As Matthew stated, "There's no way to tell with the code that you posted."  My code works.  The trick is in the implementation of the Foo class.

    class Program  
        {  
            static void Main(string[] args)  
            {  
                Bar b2 = new Bar();  
                //  
                int num = 21;  
                b2.Foo.Add(new Foo<int>(num));  
                //  
                string str = "string";  
                b2.Foo.Add(new Foo<string>(str));  
                //  
                DateTime dt = new DateTime();  
                b2.Foo.Add(new Foo<DateTime>(dt));  
                //  
                Console.WriteLine(b2.GetFooValue(0).ToString());  // 21  
                Console.ReadLine();  
            }  
        } 

    I guess I need to add a second parameter to the constructor to make it look like yours.


    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, February 4, 2009 5:35 PM
  • The trick here would be again, as RudeDog has said, in your implementation of Foo<T>. 

    1. There is no reason to explicitly inherit Foo<T> from IFoo as well as IFoo<T>.  Inheriting IFoo<T> from IFoo will force this implementation anyways. 
    2. You need to have an explicit interface implementation for IFoo.   Define an object Value property in IFoo.  Your interfaces should look like this:

    public interface IFoo<T> : IFoo

    {

        T Value { get; set; }

    }

     

    /// non generic interface

    public interface IFoo

    {

        int Number { get; set; }

        object Value { get; set; }

    }


    3. Implement the object version of Value explicitly in your derived class, and your IFoo<T> version implicitly:

    public class Foo<T> : IFoo<T>

    {

        object IFoo.Value { get; set; }

     

        public T Value

        {

            get

            {

                return (T)((IFoo)this).Value;

            }

            set

            {

                ((IFoo)this).Value = value;

            }

        }

     

        public int Number { get; set; }

    }


    This implementation above works just fine.  Notice that the implementation of the generic version of Value simply casts the non-generic version at it's base. 

    The key to remember here is that you can still cause a "Foo-Bar" situation to happen if you accidentally set the non-generic version of Value to a type that is not allowed according to the type restriction in Foo<T>.

    Best of luck.


    David Morton - http://blog.davemorton.net/
    Wednesday, February 4, 2009 5:51 PM
  • Dutchdre said:

    Hi Rudedog2,

    In your code it's not possible to fill the list Foo with instances of Foo<string> and Foo<int> together.
    Here is what i have:

    public class Bar  
    {  
        public List<IFoo> Foo;  
     
        public Bar()  
        {  
            Foo.Add(new Foo<string>() { Number = 1, Value = "Test" });  
            Foo.Add(new Foo<int>() { Number = 2, Value = 100 });  
            Foo.Add(new Foo<DateTime>() { Number = 3, Value = new DateTime(2009, 2, 4) });  
        }  
     
        public object GetFooValue(int fooNumber)  
        {  
            IFoo foo = (IFoo)this.Foo.Find(delegate(IFoo f) { return r.Number == fooNumber; });  
            return foo.Value;  
        }  
    }  
     

    Error:
    'IFoo' does not contain a definition for 'Value' and no extension method 'Value' accepting a first argument of type 'IFoo' could be found (are you missing a using directive or an assembly reference?)


     


     



    You never initialize Foo anywhere. 

    Foo = new List<IFoo>();

    Is this homework?

     public class Bar  
        {  
            public List<IFoo> Foo;  
            public Bar()  
            {  
                this.Foo = new List<IFoo>();  
            }  
     
            public object GetFooValue(int fooNumber)  
            {  
                return Foo[fooNumber].Number as object;  
            }  
        } 

    Mark the best replies as answers. "Fooling computers since 1971."
    Wednesday, February 4, 2009 6:04 PM
  • Rudedog2 said:

    You never initialize Foo anywhere. 

    Foo = new List<IFoo>();

    Is this homework?

    This is definitely not for homework, but i've replaced the real names by foo and bar and didn't test it only if it compiles.

    The real project contains a lot more details but this is a simplified version of our problem.
    Wednesday, February 4, 2009 9:02 PM
  • Dutchdre said:

    Rudedog2 said:

    You never initialize Foo anywhere. 

    Foo = new List<IFoo>();

    Is this homework?

    This is definitely not for homework, but i've replaced the real names by foo and bar and didn't test it only if it compiles.

    The real project contains a lot more details but this is a simplified version of our problem.



    Okay.  I don't mind helping with homework, as long as you were trying.  It just puts a different perspective on how to answer. 

    Speaking of answers, have you found one yet?  You might not initialize the field, but you are still asking for access to a member of a different type.  That's hard to answer.

    David cautions about type constraints and restrictions above.  I don't know if you have any in your real project.  I don't see any "where" clauses here.  Whenever I don't use specific constraints---I try to avoid it but with the .NET types there's now way around it except for wrappers---I try to implement "restraints" through overloaded constructors.

     /// Concrete class  
        public class Foo<T> : IFoo<T>  
        {  
            private T Tvalue;  
            private int Tnumber;  
     
            public Foo(int obj)  
            {  
                Type type = obj.GetType();  
                Tvalue = (T)System.Activator.CreateInstance(type);  
                Tnumber = obj;  
            }  
            public Foo(int num, int obj)  
            {  
                Type type = obj.GetType();  
                Tvalue = (T)System.Activator.CreateInstance(type, true);              
                Tnumber = num;  
            }  
     
            public Foo(string obj)  
            {  
                Type type = obj.GetType();  
                char[] c = obj.ToCharArray();  
                Tvalue = (T)System.Activator.CreateInstance(type, new object[] { c });  
                Tnumber = obj.Length;  
            }  
            public Foo(int num, string obj)  
            {  
                Type type = obj.GetType();  
                char[] c = obj.ToCharArray();  
                Tvalue = (T)System.Activator.CreateInstance(type, new object[] { c });  
                Tnumber = num;  
            }  
     
            public Foo(DateTime obj)  
            {  
                Type type = obj.GetType();  
                Tvalue = (T)System.Activator.CreateInstance(type);  
                Tnumber = obj.Millisecond;  
            }  
            public Foo(int num, DateTime obj)  
            {  
                Type type = obj.GetType();  
                Tvalue = (T)System.Activator.CreateInstance(type);  
                Tnumber = num;  
            }  
     
     
            // code ...  
            #region IFoo<T> Members  
     
            T IFoo<T>.Value  
            {  
                get 
                {  
                    return this.Tvalue;  
                }  
                set 
                {  
                    this.Tvalue = value;  
                }  
            }
            #endregion  
     
            #region IFoo Members  
     
            public int Number  
            {  
                get 
                {  
                    return this.Tnumber;  
                }  
                set 
                {  
                    this.Tnumber = value;  
                }  
            }  
            public object Value  
            {  
                get 
                {  
                    return this.Tvalue as object;  
                }  
            }
            #endregion  
        } 

    I had already used an interface similar to David's goofing around eating a burger.  I made this class implement his definitions above.


    Mark the best replies as answers. "Fooling computers since 1971."
    • Edited by Rudedog2 Wednesday, February 4, 2009 9:13 PM edit
    Wednesday, February 4, 2009 9:07 PM
  • David,

    Your solution seems to work, i'm now able to read and write the Value property even when I don't know the generic type.
    When inspecting the Foo instance the type of the value is "object {bool}" or "object {string}" etc. What is the difference between this and a normal type like "bool" and "string".

    This code is used in a readXml method of a class so we could load the xml from our legacy system correctly into our new .NET code.

    I'll discuss your solution with my colleagues to morrow, for now it works fine.

    Thank you very much.

    Ps, i've to mark the Value property in the IFoo<> interface with the new keyword because of it's hiding. Is that correct or do I have to ignore this warning?
    • Edited by Dutchdre Wednesday, February 4, 2009 9:38 PM Spellcheck
    Wednesday, February 4, 2009 9:36 PM
  • Dutchdre said:

    What is the difference between this and a normal type like "bool" and "string".



    The difference is that these values are boxed when the are returned.  This is fine, however, because you would box them anyways when you return them as object from your method.

    Ps, i've to mark the Value property in the IFoo<> interface with the new keyword because of it's hiding. Is that correct or do I have to ignore this waring?

    Yes, go ahead and add the new keyword.  The only thing it'll do, however, is hide the warning.  There is no difference in the compiled code at all.


    David Morton - http://blog.davemorton.net/
    Wednesday, February 4, 2009 9:41 PM
  • I'll discuss both solutions tomorrow with my colleagues and I'll mark the answers.
    In the real project this is a very important part of our code so performance is a real issue. Are there any performance issues when boxing all the time from object {bool}, object{string} etc to bool and string?

    The generic type of Foo<T> could be one of the following types in our projects:
    - string
    - bool
    - int
    - double
    - float
    - decimal
    - DateTime

    Wednesday, February 4, 2009 9:54 PM
  • Dutchdre said:

    Are there any performance issues when boxing all the time from object {bool}, object{string} etc to bool and string?



    Yes and no.  Specifically, there are performance considerations when boxing a bool value, but a smaller impact with a string.  The reason for this has to do with the fact that string is already a reference type, but bool is not.  To work around this (at least when referencing the generic version) you could always use a private variable of type T, and return and set that for both the generic and non-generic version of Value.  Even so, you'll still have boxing and unboxing on the set for the Non-Generic version, and you would still have boxing on the return for the non-generic Value, because you'd be boxing an "int" for instance, in order to return the value. Long story short, if you're going to be referencing a non-generic version of a generic parameter, by converting it to object, you're going to do boxing sooner or later.  Many people would disagree with me here, but I personally find the performance impact to be negligable unless you're planning on boxing extremely frequently and are already in a performance-challeneged environment.

    public class Foo<T> : IFoo<T>

    {

        private T _value;

     

        object IFoo.Value

        {

            get { return _value; }

            set { _value = (T)(object)value; }

        }

     

        public T Value

        {

            get

            {

                return _value;

            }

     

            set

            {

                _value = value;

            }

        }

     

        public int Number { get; set; }

    }



    David Morton - http://blog.davemorton.net/
    • Marked as answer by Dutchdre Thursday, February 5, 2009 10:17 AM
    Wednesday, February 4, 2009 10:01 PM
  • Thank you all very much, we're moving from delphi to .net and are facing some nice challenges.
    Wednesday, February 4, 2009 10:22 PM