14

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;
  }
}
Hugo Sereno Ferreira
  • 8,600
  • 7
  • 46
  • 92
  • I am curious.. can you explain a bit what type of values you are going to return, e.g. where are they to come from, and are there _any_ inferred or explicit constraints on the func? are we to assume that only intrinsic types are expected? – Sky Sanders Feb 16 '10 at 02:55
  • As I've pointed out in a comment below, there are situations where the caller is perfectly expecting a different semantics for the function among different types. 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 Sereno Ferreira Feb 16 '10 at 15:07
  • If you're interested in a situation I need this pattern (mind you, I've seen this occurring several times in different contexts), I have an `Evaluate` of a class named `Expression`, that maps the AST of a DSL. The underlying language is dynamically typed, so the caller doesn't know the exact type `Evaluate` forehand. But if the caller forces a specific type, e.g. `Evaluate`, then the function may add some "semantic sugar", like testing for `nulls`, numbers greater than 0, non-empty lists, etc. The caller IS expecting a `bool`, regardless of what happens inside. – Hugo Sereno Ferreira Feb 16 '10 at 15:09
  • This above example, of course, is prone to design attacks. Some people will suggest that the logic I'm pointing out should be inside an explicit cast between those objects and `bool`. But imagine the added "semantic sugar" depends on the evaluation context. Then, storing the logic inside the explicit cast would require the object to store enough information only for that purpose. – Hugo Sereno Ferreira Feb 16 '10 at 15:11
  • So the context of the solution to this question _is_ intrinsic types and object and you wish to coerce variant behavior using c# 3.0? – Sky Sanders Feb 16 '10 at 18:54
  • I updated my answer in response to Hugo's additional examples. – Richard Berg Feb 18 '10 at 05:00

5 Answers5

6

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.

Community
  • 1
  • 1
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 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 Sereno Ferreira Feb 16 '10 at 01:41
  • 2
    I agree in a very general/vague sense, but this does not answer the question at all. Sometimes reality dictates that generic & specific functionality coexist at the same stack frame. (you could always copy/paste the generic parts, I suppose, but that's worse than any of the proposals here IMO) Luckily, C# supports deferred execution, which allows for hacky-but-workable patterns as seen in my answer. – Richard Berg Feb 16 '10 at 09:23
  • 2
    @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 Feb 16 '10 at 14:46
  • 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 Feb 16 '10 at 14:55
  • 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 Feb 16 '10 at 14:56
  • @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 Sereno Ferreira Feb 16 '10 at 15:00
  • @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 Feb 16 '10 at 16:14
  • @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 Feb 16 '10 at 16:28
  • @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 Feb 16 '10 at 16:42
  • Goes without saying that the principle of DRY applies equally to lambdas. If calling Function(() => "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 with FunctionString, FunctionInt, etc, their implementations would inevitably duplicate some code. – Richard Berg Feb 16 '10 at 16:59
  • @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 Feb 16 '10 at 17:10
  • 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 Feb 16 '10 at 17:18
  • @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 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 Feb 16 '10 at 17:21
  • 1
    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 Feb 16 '10 at 17:37
  • @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 Sereno Ferreira Feb 17 '10 at 11:23
  • Ok, I added two real-world examples (again, simplified) ;-) – Hugo Sereno Ferreira Feb 17 '10 at 11:44
3
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
  • 20,629
  • 2
  • 66
  • 86
  • 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 Feb 16 '10 at 16:25
  • 1
    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 (2) if not, it's still more useful than saying "don't do that." – Richard Berg Feb 16 '10 at 17:09
  • Interesting solution, but `DoSomething` 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 Sereno Ferreira Feb 17 '10 at 12:31
  • @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 Feb 18 '10 at 00:09
1

Can you use as here?

T s = "hello" as T;
if(s != null)
    return s;
Anon.
  • 58,739
  • 8
  • 81
  • 86
  • 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 Sereno Ferreira Feb 16 '10 at 01:47
  • 1
    Well then: `if (typeof(T) == typeof(string)) return "hello" as T;` – Anon. Feb 16 '10 at 02:01
  • Good call! Almost there, but the operator `as` can only be used in situations where `T` is a class. +1 nonetheless. – Hugo Sereno Ferreira Feb 16 '10 at 02:07
  • 1
    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 Feb 16 '10 at 02:31
  • 1
    @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. – Oliver Friedrich Feb 16 '10 at 14:16
  • @BeowulfOF: While it might be "safe", the question is asking how to avoid the double-cast through `object`. – Anon. Feb 16 '10 at 20:25
1

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
  • 131,090
  • 20
  • 188
  • 351
0

Ok, I took a run at it from several different angles and came up short. I would have to conclude that if your current implementation gets the job done you should take the win and move on. Short of some arcane emissions what you got is what you get.

But the compiler has no way of knowing that the previous test assured T is of type string.

Umm.... If I am not mistaken, generics is just code gen. The compiler generates a matching method for each distinct type found in the calling methods. So the compiler does know the type argument for the overload being called. Again; If I am not mistaken.

But overall, i think you are misusing the generic in this case, from what I can see, and as others have stated, there are more appropriate solutions..... which are unnamable unless you post code that completely specifies your requirements.

just my 2 pesos...

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
Sky Sanders
  • 36,396
  • 8
  • 69
  • 90
  • 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 Sereno Ferreira Feb 16 '10 at 02:00
  • 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 Feb 16 '10 at 02:10
  • 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 Feb 16 '10 at 02:12
  • 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 Sereno Ferreira Feb 16 '10 at 02:17