views:

289

answers:

5

Consider the following (heavily simplified) code:

public T Function<T>() {
    if (typeof(T) == typeof(string)) {
        return (T) (object) "hello";
    }
    ...
}

It's kind of absurd to first cast to object, then to T. But the compiler has no way of knowing that the previous test assured T is of type string.

What is the most elegant, idiomatic way of achieving this behavior in C# (which includes getting rid of the stupid typeof(T) == typeof(string), since T is string can't be used)?


Addendum: There is no return type variance in .net, so you can't make a function overload to type string (which, by the way, is just an example, but one reason why association end redefinition in polymorphism, e.g. UML, can't be done in c#). Obviously, the following would be great, but it doesn't work:

public T Function<T>() {
    ...
}

public string Function<string>() {
    return "hello";
}

Concrete Example 1: Because there's been several attacks to the fact that a generic function that tests for specific types isn't generic, I'll try to provide a more complete example. Consider the Type-Square design pattern. Here follows a snippet:

public class Entity {
  Dictionary<PropertyType, object> properties;

  public T GetTypedProperty<T>(PropertyType p) {
    var val = properties[p];

    if (typeof(T) == typeof(string) {
      (T) (object) p.ToString(this);  // magic going here
    }

    return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(val);
  }
}

Concrete Example 2: Consider the Interpreter design pattern:

public class Expression {
  public virtual object Execute() { }
}

public class StringExpression: Expression {
  public override string Execute() { }    // Error! Type variance not allowed...
}

Now let's use generics in Execute to allow the caller to force a return type:

public class Expression {
  public virtual T Execute<T>() { 
    if(typeof(T) == typeof(string)) {  // what happens when I want a string result from a non-string expression?
       return (T) (object) do_some_magic_and_return_a_string();
    } else if(typeof(T) == typeof(bool)) { // what about bools? any number != 0 should be True. Non-empty lists should be True. Not null should be True
       return (T) (object) do_some_magic_and_return_a_bool();
    }
  }
}

public class StringExpression: Expressiong {
  public override T Execute<T>() where T: string {   
    return (T) string_result;
  }
}
+6  A: 

If you're making these types of checks in a generic method, I'd rethink your design. The method is obviously not truly generic - if it were, you wouldn't need specific type checking...

Situations like this typically can be handled more cleanly by a redesign. One alternative is often to provide an overload of the appropriate type. Other design alternatives which avoid the type-specific behavior exist, as well, such as Richard Berg's suggestion of passing in a delegate.

Reed Copsey
I'm sorry. I understand your claim, but there are legitimate uses to this pattern, when you want to modify your inner logic should a specific type appear. And overloads do not work because you don't have variance in the return type of a function.
Hugo S Ferreira
Richard Berg
@Richard: I think this is the *only* valid answer to the question as written. How on earth is a caller supposed to understand the semantics of a generic method that behaves differently for specific types? Maybe if the OP had explained what he was really trying to do, somebody would have been able to offer and explain a better alternative.
Aaronaught
I think my answer is valid. Of course, I'm biased :) But in all honesty, there's nothing wrong with the caller having some insider knowledge about the callee, so long as the public interface to each class remains clear to consumers. Depending on the scenario, it may be appropriate to make the generic callee private/protected/internal. In other scenarios, a deferred execution mechanism can be an explicit part of the generic class' contract. Load System.Linq.* in Reflector for fun sometime -- I'd be shocked if they didn't use "fake partial specialization" (similar to my Example2) somewhere.
Richard Berg
Anyway, if you think my way is great / sucks terribly / something in between, let's discuss it there instead of cluttering Reed's comments.
Richard Berg
@Aaronaugh: I still have to analyze Richard's answer, but bear in mind that the caller may now forehand that the semantics of the function differ among types, and naturally expect that. For example, several languages support the `+` operator for `integers` and `strings`; and yet, for `integers` it acts as a sum, while for `strings` it acts as a concatenator.
Hugo S Ferreira
@Hugo S Ferreira: But your example is flawed - When you use + with integer vs. + with strings, you're calling TWO SEPARATE OPERATORS, defined in two separate locations. It's more like an overload, which was my suggestion here. If "+" was defined using a single method, and worked differently for two different types, there would be a lot of confusion in the language specification...
Reed Copsey
@Reed, you're totally correct that method overloading is the idiomatic C# way of achieving type variance. [likewise for overloaded operators, which are just shorthand for method dispatch] All I'm saying is it's not the ONLY way. C# delegates, particularly with 3.0 syntax, are equally capable of providing generic routines with customized dispatch, including type-variant dispatch. Depending on your overall design, they may or may not be a better fit (for some definition of "better" -- flexible, legible, maintainable...) But there's no question they're a better answer to Hugo's question.
Richard Berg
@Richard Berg: Perhaps, perhaps not in this situation. In his example, you'd always be passing a lambda that returned "Hello" from ANY call site, which would violate DRY. Without seeing more "real" code, it's difficult to know what the best design option would be. I was just suggesting that the OP's design is probably NOT the appropriate one, since it's making a generic method non-generic, and if you want the method to be non-generic, overloads are a better abstraction.
Reed Copsey
Goes without saying that the principle of DRY applies equally to lambdas. If calling Function<T>(() => "Hello") is something you do more than once or twice, it should probably be wrapped into a helper method. Same way you do with any other parameter whose call-sites start to feel like boilerplate. //// Meanwhile, I'd argue that a solid mechanism for mixed generic + specific codepaths is a huge win for DRY. If you blindly replaced Function<T> with FunctionString, FunctionInt, etc, their implementations would inevitably duplicate some code.
Richard Berg
PS I tried to remove my -1 earlier but it wouldn't let me.
Richard Berg
@Richard Berg: Wrapping the Function in the call site can work, provided it's only being used by a single consumer. It really depends on what the OP is trying to do, though - I reworded my answer (and linked to yours), see if you like it better now.
Reed Copsey
Thanks, downvote removed. //// I don't know what you mean by "provided it's only being used by a single consumer." Consider the Example1 class from my answer. You could add public wrappers Calculate(string s) and Calculate(int i) that encapsulated the custom error handlers seen in Main(). Then any number of consumers could call Calculate(1234), Calculate(5678), Calculate("asdf"), Calculate(1234, i => E_FAIL), etc...
Richard Berg
@Richard Berg: Yeah - but then you're just adding overloads ;) The generic method is just an internal, implementation detail at that point. If you leave the Func<T> in the calling class (Tester), a different class would have to reimplement it, violating DRY. If you move it into Example1, you're making overloads (which was my answer).
Reed Copsey
The core issue is whether related codepaths share a common implementation whenever possible; and if so, how. Unless you know how to workaround C#'s variance limitations (eg with delegates), adding overloads is just as likely to *break* DRY as improve it.
Richard Berg
@Reed, this whole conversation is flawed based on the premise that overloading is possible. Let's clear this out: .NET doesn't support covariance (nor contravariance) in return types. Period. The function arguments, in my example, are `void`. What you are suggesting with overloads is that either: (a) in the same class, I make two functions with the same name that return different types (not!) or (b) I create a second class that derives the first, and override the function to make it type stronger. Both can't be done. See another example in a comment to my original post.
Hugo S Ferreira
Ok, I added two real-world examples (again, simplified) ;-)
Hugo S Ferreira
+1  A: 

Can you use as here?

T s = "hello" as T;
if(s != null)
    return s;
Anon.
Not without a generic constraint to class...
Reed Copsey
Good point. That may or may not be a deal-breaker here.
Anon.
Sorry, but "hello" should be "conditionally" executed when T is string, not everytime and just tested for string. Of course, "hello" here is just an example; I don't really want to go all this way just to return some const ;-)
Hugo S Ferreira
Well then: `if (typeof(T) == typeof(string)) return "hello" as T;`
Anon.
Good call! Almost there, but the operator `as` can only be used in situations where `T` is a class. +1 nonetheless.
Hugo S Ferreira
almost sounds as if hugo is just inciting a brainstorming riot and actually has a solution and is waiting for everyone to take a ride on the failboat before he breaks it out... lolz..
Sky Sanders
@Anon: if you already checked that T == typeof(string) than do not use the as operator for cast, it is save and correct to use direct cast here.
BeowulfOF
@BeowulfOF: While it might be "safe", the question is asking how to avoid the double-cast through `object`.
Anon.
+1  A: 

I can't think of an "elegant" way to do this. As you say, the compiler can't know that the conditional has ensured that the type of T is string. As a result, it has to assume that, since there's no generalized way to convert from string to T, it's an error. object to T might succeed, so the compiler allows it.

I'm not sure I'd want an elegant way to express this. Although I can see where it'd be necessary to do explicit type checks like this in some situations, I think I'd want it to be cumbersome because it really is a bit of a hack. And I'd want it to stick out: "Hey! I'm doing something weird here!"

Jim Mischel
A: 
Sky Sanders
Ah! There's a problem: generics are not just code gen in c#. They are a completely different beast from templates in C++. I'll try to provide a better example of "good usage", but I doubt this would render alternate designs easier to suggest (though people would probably refrain to frowning their eyes ;-)
Hugo S Ferreira
Umm.. unless i am mistaken, you are mistaken. ;-). Here is a quick, non overly verbose definition which I believe is correct: http://www.codeproject.com/KB/cs/generics_explained.aspx specifically the 'How generics are handled by the .NET runtime' section. Am I missing something?
Sky Sanders
Ok... i see your point.. .net generics are generated at runtime. Brain fart. so now that i am totally turned around... let me try to wrap my head around this again from a different angle
Sky Sanders
You're not completely wrong, though. Just that stating generics as just code generation is an oversimplification. See this list as an example of the difference between generics and templates: http://msdn.microsoft.com/en-us/library/c6cyy67b(VS.80).aspx
Hugo S Ferreira
+2  A: 
using System;
using System.Collections.Generic;
using System.Linq;

namespace SimpleExamples
{
    /// <summary>
    /// Compiled but not run.  Copypasta at your own risk!
    /// </summary>
    public class Tester
    {
        public static void Main(string[] args)
        {
            // Contrived example #1: pushing type-specific functionality up the call stack
            var strResult = Example1.Calculate<string>("hello", s => "Could not calculate " + s);
            var intResult = Example1.Calculate<int>(1234, i => -1);

            // Contrived example #2: overriding default behavior with an alternative that's optimized for a certain type
            var list1 = new List<int> { 1, 2, 3 };
            var list2 = new int[] { 4, 5, 6 };
            Example2<int>.DoSomething(list1, list2);

            var list1H = new HashSet<int> { 1, 2, 3 };
            Example2<int>.DoSomething<HashSet<int>>(list1H, list2, (l1, l2) => l1.UnionWith(l2));
        }
    }

    public static class Example1
    {
        public static TParam Calculate<TParam>(TParam param, Func<TParam, TParam> errorMessage)            
        {
            bool success;
            var result = CalculateInternal<TParam>(param, out success);
            if (success)
                return result;
            else
                return errorMessage(param);
        }

        private static TParam CalculateInternal<TParam>(TParam param, out bool success)
        {
            throw new NotImplementedException();
        }
    }

    public static class Example2<T>
    {
        public static void DoSomething(ICollection<T> list1, IEnumerable<T> list2)
        {
            Action<ICollection<T>, IEnumerable<T>> genericUnion = (l1, l2) =>
            {
                foreach (var item in l2)
                {
                    l1.Add(item);
                }
                l1 = l1.Distinct().ToList();
            };
            DoSomething<ICollection<T>>(list1, list2, genericUnion);
        }

        public static void DoSomething<TList>(TList list1, IEnumerable<T> list2, Action<TList, IEnumerable<T>> specializedUnion)
            where TList : ICollection<T>
        {
            /* stuff happens */

            specializedUnion(list1, list2);

            /* other stuff happens */            
        }
    }
}

/// I confess I don't completely understand what your code was trying to do, here's my best shot
namespace TypeSquarePattern
{
    public enum Property
    {
        A,
        B,
        C,
    }

    public class Entity
    {
        Dictionary<Property, object> properties;
        Dictionary<Property, Type> propertyTypes;

        public T GetTypedProperty<T>(Property p) 
        {
            var val = properties[p];
            var type = propertyTypes[p];

            // invoke the cast operator [including user defined casts] between whatever val was stored as, and the appropriate type as 
            // determined by the domain model [represented here as a simple Dictionary; actual implementation is probably more complex]
            val = Convert.ChangeType(val, type);  

            // now create a strongly-typed object that matches what the caller wanted
            return (T)val;
        }
    }
}

/// Solving this one is a straightforward application of the deferred-execution patterns I demonstrated earlier
namespace InterpreterPattern
{
    public class Expression<TResult>
    {
        protected TResult _value;             
        private Func<TResult, bool> _tester;
        private TResult _fallback;

        protected Expression(Func<TResult, bool> tester, TResult fallback)
        {
            _tester = tester;
            _fallback = fallback;
        }

        public TResult Execute()
        {
            if (_tester(_value))
                return _value;
            else
                return _fallback;
        }
    }

    public class StringExpression : Expression<string>
    {
        public StringExpression()
            : base(s => string.IsNullOrEmpty(s), "something else")
        { }
    }

    public class Tuple3Expression<T> : Expression<IList<T>>
    {
        public Tuple3Expression()
            : base(t => t != null && t.Count == 3, new List<T> { default(T), default(T), default(T) })
        { }
    }
}
Richard Berg
Richard: +1 I like this, as a design, but it's answering a different question than the OP, in my opinion. You're (correctly, I feel) purposely pulling the type specific info out of the generic routines, by passing a delegate through to handle that from the caller side. However, the generic method here does not do anything "non-generic" - it has no knowledge of type. This is a good option for a design that avoids the issues I raised, however.
Reed Copsey
Fair point. Strictly speaking, I too am suggesting that Hugo redesign his interface, just as you did. However: (1) in all likelihood, my solution isn't too far from what Hugo had in mind [especially if he is coming from C++ templates, or a functional language] for his Function<T> (2) if not, it's still more useful than saying "don't do that."
Richard Berg
Interesting solution, but `DoSomething<TList>` returns `void`, and the problem I'm having is related to return type variance. Unless I'm modifying an existing object (in your example, argument `TList` list1), I can't extrapolate this to a situation where you want the return type to change.
Hugo S Ferreira
@Hugo - achieving return type variance is really no different from the variance demonstrated in my initial examples. Anyway, I edited my post to show how you might implement the two patterns you added to the question. Let me know if that helps.
Richard Berg