-1

I have the following method:

public static Object[] GetCategories() {
  return new [] {
    new { Id = 1, Name = "A" }, 
    new { Id = 2, Name = "B" },
    new { Id = 2, Name = "BA" },
    new { Id = 2, Name = "BC" }
  }
}

On the same class I have another method:

public static Object[] GetPosts() {

  var categories = GetCategories() ...

}

On this method I need to get the Categories which Name contains the letter "A".

How can I do this if GetCategories is returning, and must return, a Object array.

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • 4
    First question why must it return an object array? If you really really have to then you can't do it in a type safe manner. That usually means you should stop and refactor...however there is the option of casting them to dynamic objects and just access the property you know is there or you could use reflection. Both of those options I would not recommend in this scenario. C# is stronly typed for the good! make use of it – Dave Oct 19 '18 at 16:19
  • 3
    If you have 4 things with an `Id` and a `Name` then you should have the return be a type/abstraction that fits those properties. Dont work with `object` like this... – maccettura Oct 19 '18 at 16:20
  • I agree with the other comments. How is `GetCategories` really populated? Why can't it return something strongly typed? If all else fails, you can cast to `dynamic`, but I'd consider that a last resort. There's a lot of potential for run-time errors when using `dynamic` – Matt Burland Oct 19 '18 at 16:23
  • The reason is beacuse I am using these methods to seed a database using Entity Framework 2.1 Core which has a new HasData method that do work in seeding related entities the data must be an array of anonymous objects. So there is nothing I can do ... – Miguel Moura Oct 19 '18 at 16:26
  • `GetCategories().Where(x => ((string)x.GetType().GetProperty("Name").GetValue(x, null)).Contains("A"))` see https://stackoverflow.com/a/1197004/1462295 but really, use a real Type – BurnsBA Oct 19 '18 at 17:19
  • probably `GetCategories().Where(o => o.Name.Contains("A")).ToArray()` – Slai Oct 19 '18 at 18:23

1 Answers1

4

How can I do this if GetCategories is returning, and must return, a Object array.

You can't. Object is devoid of the necessary type information, so you must resort to either reflection or casting to an explicit (non-anonymous) type.

As we can't use an implicit type (var) in method signatures in C# (unlike in C++ where you can use auto) we must either use a Value-Tuple (a C# 7.2 feature) or manually define your object type.

C# 7.2 Value-Tuple:

public static (Int32 id, String name)[] GetCategories()
{
    return new[]
    {
        ( 1, "A" ),
        ( 2, "BA" ),
        ( 3, "BAC" ),
    };
}

Manual type:

public class /* or struct */ Category
{
    public Category( Int32 id, String name )
    {
        this.Id = id;
        this.Name = name;
    }

    public Int32 Id { get; }
    public String Name { get; }
}

public static Category[] GetCategories()
{
    return new[]
    {
        new Category( 1, "A" ),
        new Category( 2, "BA" ),
        new Category( 3, "BAC" ),
    };
}

You said the method's signature must return Object[], but if we return a value-tuple or custom type then we can still cast to it, albiet without compile-time type safety:

public Object[] GetCategories()
{
    return new[]
    {
        new Category( 1, "A" ),
        new Category( 2, "BA" ),
        new Category( 3, "BAC" ),
    };
}

IEnumerable DoSomething()
{
    return this.GetCategories()
        .Cast<Category>()
        .Where( c => c.Name.Contains("A") )
}

Or:

public Object[] GetCategories()
{
    return new (Int32 id, String name)[]
    {
        new ( 1, "A" ),
        new ( 2, "BA" ),
        new ( 3, "BAC" ),
    };
}

IEnumerable DoSomething()
{
    return this.GetCategories()
        .Cast<(Int32 id, String name)>()
        .Where( c => c.Name.Contains("A") )
}

In my opinion we should always prefer Value-Tuples over anonymous types anyway, they have numerous advantages over C#'s anonymous types:

  • You can use them in method signatures as return types or parameter types.
  • They're immutable.
  • They're value-types (not heap-allocated objects) which avoids unnecessary allocation.
  • You can use them whenever you'd use anonymous types.

The only downside is opening yourself up to a Lilliputian war about whether value-tuple members should be PascalCase or camelCase.

BTW, if your data is immutable then you should return a static readonly IReadOnlyList[] instead of return new[]... to avoid extraneous unnecessary heap allocations on every call and to prevent unwanted mutation of static state:

private static readonly IReadOnlyList[] _categories = new[]
{
    ...
};

public static IReadOnlyList[] GetCategories() => _categories;
Dai
  • 141,631
  • 28
  • 261
  • 374
  • 1
    "You can't use an implicit type (var) on a method boundary in C# (unlike in C++ where you can use auto). You must either use a Value-Tuple (a C# 7.2 feature) or manually define your object type" - what do you mean? While not recommended OP's code does compile (with a semi colon added here and there) – Dave Oct 19 '18 at 16:35
  • @Dave C# disallows (by design) the use of `var` in a method signature, so you can't do `public var GetFoo(var x) { return new { Baz = x; }` which is what the OP was asking for. – Dai Oct 19 '18 at 16:37
  • Don't forget that you can also work around this by using the `dynamic` type, even though you completely lose typing information that way. – laptou Oct 19 '18 at 17:54
  • Still not following. OPs sample code doesn't try and use var as a return type and they don't ask about that. Anyway this is an aside, good answer – Dave Oct 19 '18 at 22:23