2

Why this simple code doesnt work?

var abc = new[]{1,2,3,4}.Select(x => default(typeof(x)));

i expect something like array of zeros but get compiler error. So how i can default values in lambda expressions?

In my real application i have meta class with type Type

public class FieldInfo
{
     public Type type
}

I get IEnumerable<FieldInfo> as parameter to my method and want return an array of default values for each type (object[])

Neir0
  • 12,849
  • 28
  • 83
  • 139
  • 1
    what (the hell) are you trying to do? :). in other words, what value do you expect in `abc`? – bas Mar 03 '13 at 20:35
  • 1
    What error do you get? if you know its an int, why do you use `typeof`? – YavgenyP Mar 03 '13 at 20:35
  • If you just want an int array of size 4 all set to zero then just create the int array. It will initialise each element to zero anyway. – Steve Mar 03 '13 at 20:37
  • What's the compiler error? – ChrisF Mar 03 '13 at 20:37
  • @bas i have a more complex code it just a simplified example – Neir0 Mar 03 '13 at 20:40
  • @Neir0 that I believe :). But the problem is that we have no clue about what you are trying to achieve. Can you try to make that clear? E.g. by specifying your expected result – bas Mar 03 '13 at 20:42
  • Hm, the `default` for class types is always null, I'm not sure what you're expecting as output if it's all returned in an object[]. Objects created using the default constructor? – Joachim Isaksson Mar 03 '13 at 20:51
  • @Neir0 doesn't Jon Skeets answer solve your problem then? He provides an extension method `SelectDefaults` with generics. Maybe you can get rid of your `Type` field all together. It remains a bit abstract to me. Maybe others can have less difficulties to follow. – bas Mar 03 '13 at 20:53
  • @Joachim Isaksson but not null for structs. – Neir0 Mar 03 '13 at 20:54
  • @Neir0 oops i made a blunder.. – nawfal Mar 03 '13 at 20:54
  • @bas No, because John's solution return default value for collection type not for each element of my collection. – Neir0 Mar 03 '13 at 20:56
  • 1
    @Neir0: No, it returns the default value for the *element* type of the collection type. Now you've actually said what you're trying to achieve, it will be easier to implement. (I've edited my answer.) – Jon Skeet Mar 03 '13 at 20:58

4 Answers4

6

This is the problem:

typeof(x)

You're using the typeof operator as if it's a method call, taking a value. It's not. It needs a compile-time type name or type parameter (or unbound type such as Dictionary<,>, or void).

The same is try with the default operator.

So that's what's wrong - but without knowing what you're trying to achieve, we can't actually advise you on how to fix it. You might just want something like this:

public static IEnumerable<T> SelectDefault<T>(this IEnumerable<T> source)
{
    return source.Select(x => default(T));
}

Then this:

var abc = new[]{1,2,3,4}.SelectDefault().ToArray();

... would give you an int[] with all values zero.

EDIT: Now we have more information, it's easier to provide what you want, which is basically a method to get the default value from a Type, boxing as appropriate:

public static object GetDefaultValue(Type type)
{
    // Non-nullable value types should be boxed, basically
    if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
    {
        // Not the swiftest approach in the world, but it works...
        return Activator.CreateInstance(type);
    }

    // Everything else defaults to null
    return null;
}

So you'd have something like:

var defaults = fields.Select(field => GetDefaultValue(field.type));

(Where field is a FieldInfo as per the question - I'm deliberately not calling GetType() here.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
3

You can't get the default value at runtime this way, default() and typeof() are compile time features. But you can try this suggestion. It uses Activator.CreateInstance to get the default value for a value type at runtime.

Community
  • 1
  • 1
Fox32
  • 13,126
  • 9
  • 50
  • 71
3

Do you mean this?

var abc = new[]
{
    1, 2, 3, 4
}
.Select(x = > {
    var xType = x.GetType();
    if (xType.IsValueType)
    {
        return Activator.CreateInstance(xType);
    }
    else
    {
        return null;
    }
})
aush
  • 2,108
  • 1
  • 14
  • 24
  • Yeah! That is what im looking! Did not excpect that it is so hard to do. – Neir0 Mar 03 '13 at 20:59
  • Note that this will also create an instance of `Nullable` etc, which will then be boxed to null. – Jon Skeet Mar 03 '13 at 23:05
  • @Jon Skeet, I've checked that `Activator.CreateInstance(new int?(1).GetType())` actually returns 0 (apparently, because `new int?(1).GetType()` is `Int32`). So, one would never get a boxed to null instance this way which is different from an `Activator.CreateInstance(typeof(Nullable))`. Am I missing something? – aush Mar 04 '13 at 02:26
  • @aush: You're right - if we're calling `GetType()` as per your answer, it wouldn't be a problem. However, with the OP's actual scenario where the type is from a separate property, it could be. – Jon Skeet Mar 04 '13 at 07:36
2

The compiler needs a compile-time type:

var abc = new[]{1,2,3,4}.Select(x => default(Int32));

will generate all 0's. I have no idea what you are trying to achieve with this, though, because all this does is return an empty array with the default value.

CC Inc
  • 5,842
  • 3
  • 33
  • 64