none
using the 'is' operator with generics RRS feed

  • Question

  • Hello,

    I have the following generic function defined. When I try to determine the type of the generic operator using the 'is' keyword it fails.

    public static T DoSomething<T>(string s) where T:class

    {

        T requestedinstance=default(T);

        if (T is A)

        {

          // do something that actually assigns a value to requestedinstance

        }

       else if (T is B)

        {    

           do something that actually assigns a value to requestedinstance

        }

       return requestedinstance;

    }

    basically what i want to achieve is to be able to call this function with 2-3 difference types. I want to then find out what type has been used to make the call and accordingly make the assignment to the return value: requestedinstance.

    However, both the if and else if checks fail when the function is called as DoSomething<A>("test"); or as DoSomething<B>("test");

     

    Not sure why is this behavior seen ? is this by design ? I would think once the function has been called with a specific generic type parameter that you would be able to determine it inside the function, but apparently not.

    Any thoughts ? Am I missing something ? Thanks.

     

     

     

     

    Wednesday, June 23, 2010 7:45 PM

Answers

  • delzy,

    I understood your question correctly, sorry that the answer was not communicating well enough what I was trying to say. Hopefully, Chris clarified that.

    Here is the updated snippet you could use (the "is" keyword does not fit the bill) to get what you want, assuming A is Mine.MyTypeA, B is Mine.MyTypeB, is as follows:

    public static T DoSomething<T>(string s) where T:class

    {

        T requestedinstance=default(T);

        if (typeof(T).Equals(typeof(Mine.MyTypeA))

        {

          // do something that actually assigns a value to requestedinstance

        }

       else if (typeof(T).Equals(typeof(Mine.MyTypeB))

        {   

           do something that actually assigns a value to requestedinstance

        }

       return requestedinstance;

    }


    By the way your snippet is still incorrect as default(T) always produces null for reference types. Look at Chris's sample for more info.

    Architecturally, what you are trying to do is of questionable value. Consider replacing conditional statements with polymorphism.
    Please mark this post as helpful if it is (or even mark it as an answer, if appropriate).
    • Marked as answer by delzy Thursday, June 24, 2010 2:45 PM
    Thursday, June 24, 2010 5:49 AM

All replies

  • You are using "is" with instances of types, not with types themselves, as you probably should (types have the System.Type type -- sorry for overusing that word).

    You could write the following though:

    if (typeof(T).Equals(A))

    {

    }

    You might also look into typeof(T).IsAssignableFrom(A); // or the reverse, I don't recall -- typeof(A).IsAssignableFrom(T)

    I am assuming A is of type System.Type.

     

    That said, it's not clear why you need to use generics there or is it even appropriate.


    Please mark this post as helpful if it is (or even mark it as an answer, if appropriate).
    Wednesday, June 23, 2010 7:50 PM
  • _Mikhail,

     

    The purpose really is to be able to assign/compute a value to/for the variable requestedinstance depending on the type of the generic parameter T.

    I think using the "is" keyword requires you to work with objects/instances, doesnt it ? It's like "if obj is of type Type". And here my object is of type T, which is generic and can have 2-3 possible values. So not sure what you mean by:"

    "You are using "is" with instances of types, not with types themselves, as you probably should (types have the System.Type type -- sorry for overusing that word)."

    Thanks for answering.

     

     

    Wednesday, June 23, 2010 8:27 PM
  • The 'is' keyword requires an instance on the left and a type name on the right. In your case, you are taking T (a type name) and comparing that to another type name, which does not work. Here are a few examples of ways that you could perform a check against T.

            public static T DoSomething<T>() where T: new()   
            {
                T instance = default(T);
                T createdInstance = new T();
                A instanceOfA = new A();

                if (instance is A) //This will never be true for a class type because default(T) is null
                    return instance;

                if (instanceOfA is T) //This will be true when T == A
                    return (T)(object)instanceOfA;            

                if (createdInstance is A) //This will be true when T == A and uses an instance
                    return createdInstance;

                if (typeof(T) == typeof(A)) //This will be true when T == A without creating an instance to verify
                    return new T();

                if (typeof(T).IsAssignableFrom(typeof(A))) //This will be true if you could assign an instance of A to an instance of T
                    return new T();

                return default(T);
            }

    Wednesday, June 23, 2010 10:07 PM
  • Okay, first of all I apologize for the typos. My code snippet should have been like:

     

    public static T DoSomething<T>(string s) where T:class

    {

        T requestedinstance=default(T);

        if (requestedinstance is A)

        {

          // do something that actually assigns a value to requestedinstance

        }

       else if (requestedinstance is B)

        {    

           do something that actually assigns a value to requestedinstance

        }

       return requestedinstance;

    }

     

    I also think I answered the question myself, well atleast to some extent. The type checks fail above (requestedinstance is neither A nor B) because requestedinstance is only "declared" but not yet "initialized". This is not just true for generic types, but for any other type.

    Wednesday, June 23, 2010 10:21 PM
  • delzy,

    I understood your question correctly, sorry that the answer was not communicating well enough what I was trying to say. Hopefully, Chris clarified that.

    Here is the updated snippet you could use (the "is" keyword does not fit the bill) to get what you want, assuming A is Mine.MyTypeA, B is Mine.MyTypeB, is as follows:

    public static T DoSomething<T>(string s) where T:class

    {

        T requestedinstance=default(T);

        if (typeof(T).Equals(typeof(Mine.MyTypeA))

        {

          // do something that actually assigns a value to requestedinstance

        }

       else if (typeof(T).Equals(typeof(Mine.MyTypeB))

        {   

           do something that actually assigns a value to requestedinstance

        }

       return requestedinstance;

    }


    By the way your snippet is still incorrect as default(T) always produces null for reference types. Look at Chris's sample for more info.

    Architecturally, what you are trying to do is of questionable value. Consider replacing conditional statements with polymorphism.
    Please mark this post as helpful if it is (or even mark it as an answer, if appropriate).
    • Marked as answer by delzy Thursday, June 24, 2010 2:45 PM
    Thursday, June 24, 2010 5:49 AM
  • Thanks _Mikhail. I think your solution would work. Actually, I am using this method to feed my unit tests. I deserialize an xml file in this method, and depending on the generic type passed while calling the question I populate requestedinstance and return it to the caller.
    Thursday, June 24, 2010 2:44 PM
  • delzy, you are very welcome.

    Two more things: 1) it's better to throw an exception than to return null (encountering a null is not communicating what went wrong to the caller) 2) As far as I remember, you can bind your serializer to T and make an explicit cast after deserialization, no need to make distiction between types. Check out the following class stub I wrote (commented code - for XmlSerializer):
    public class Serializer<T>
        {
            private DataContractSerializer _serializer;
            //private XmlSerializer _serializer;

            public Serializer()
            {
                _serializer = new DataContractSerializer(typeof(T));
                //_serializer = new XmlSerializer(typeof(T));
            }

            public T DeserializeFromFile(string fullPathForFile)
            {
                //TODO: put exception handling in, check arguments and throw if they are invalid.

                using (TextReader textReader = File.OpenText(fullPathForFile))
                {
                    using (XmlTextReader xmlReader = new XmlTextReader(textReader))
                    {
                        return (T)_serializer.ReadObject(xmlReader);
                        //return (T)_serializer.Deserialize(xmlReader);
                    }
                }
            }

            public void SerializeToFile(string fullPathForFile, T objectGraphToSerialize)
            {
                //TODO: put exception handling in, check arguments and throw if they are invalid.

                using (TextWriter textWriter = File.CreateText(fullPathForFile))
                {
                    using (XmlTextWriter xmlWriter = new XmlTextWriter(textWriter))
                    {
                        xmlWriter.Formatting = Formatting.Indented;
                        _serializer.WriteObject(xmlWriter, objectGraphToSerialize);
                        //_serializer.Serialize(xmlWriter, objectGraphToSerialize);
                    }
                }
            }
        }
    Please mark this post as helpful if it is (or even mark it as an answer, if appropriate).
    Thursday, June 24, 2010 7:18 PM