Есть ли способ вернуть анонимный тип из метода? - PullRequest
60 голосов
/ 25 августа 2009

Я знаю, что не могу написать такой метод, как:

public var MyMethod()
{
   return new{ Property1 = "test", Property2="test"};
}

Я могу сделать это иначе:

public object MyMethod()
{
   return new{ Property1 = "test", Property2="test"}
}

но я не хочу делать второй вариант, потому что, если я это сделаю, мне придется использовать отражение.

<ч />

Почему я хочу это сделать:

Сегодня у меня на странице aspx есть метод, который возвращает данные в качестве результата, и я не могу изменить его , я пытался преобразовать этот DataTable в метод Anonymous со свойствами, с которыми я хочу работать , Я не хотел создавать класс только для этого, и, поскольку мне нужно будет выполнять один и тот же запрос более одного раза, я подумал, что создать метод, который возвращает анонимный тип, будет хорошей идеей.

Ответы [ 11 ]

63 голосов
/ 25 августа 2009

Возвращать его как System.Object - это только способ вернуть анонимный тип из метода. К сожалению, нет другого способа сделать это, так как анонимные типы были разработаны специально для предотвращения их использования таким образом.

Есть несколько трюков, которые вы можете сделать в сочетании с возвратом Object, которые позволяют вам приблизиться. Если вы заинтересованы в этом обходе, прочитайте Не удается вернуть анонимный тип из метода? В самом деле? .

Отказ от ответственности: Несмотря на то, что в статье, на которую я ссылался, есть обходной путь, который не означает, что это хорошая идея. Я настоятельно рекомендую вам использовать этот подход, когда создание обычного типа будет безопаснее и проще для понимания.

24 голосов
/ 04 февраля 2014

Кроме того, вы можете использовать класс Tuple в .NET 4.0 и выше:

http://msdn.microsoft.com/en-us/library/system.tuple(v=vs.110).aspx

Tuple<string, string> Create()
{
return Tuple.Create("test1", "test2");
} 

тогда вы можете получить доступ к таким свойствам:

var result = Create();
result.Item1;
result.Item2;
18 голосов
/ 12 июля 2012
public object MyMethod() 
{
    return new
    {
         Property1 = "test",
        Property2 = "test"
     };
}

static void Main(..)
{
    dynamic o = MyMethod();  
    var p1 = o.Property1;
    var p2 = o.Property2;
}
15 голосов
/ 25 августа 2009

Самое простое решение - создать класс, поместить значения в свойство, а затем вернуть его. Если анонимные типы усложняют вашу жизнь, значит, вы неправильно их используете.

11 голосов
/ 09 августа 2018

В качестве альтернативы, начиная с C # 7, мы можем использовать ValueTuple . Небольшой пример из здесь :

public (int sum, int count) DoStuff(IEnumerable<int> values) 
{
    var res = (sum: 0, count: 0);
    foreach (var value in values) { res.sum += value; res.count++; }
    return res;
}

А на приемном конце:

var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");

Или:

var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");
8 голосов
/ 12 мая 2016

Несмотря на предупреждения о том, является ли это хорошей идеей или нет ... A dynamic, похоже, прекрасно работает для частного метода.

void Main()
{
    var result = MyMethod();
    Console.WriteLine($"Result: {result.Property1}, {result.Property2}");
}

public dynamic MyMethod()
{
    return new { Property1 = "test1", Property2 = "test2" };
}

Вы можете запустить этот пример в LinqPad. Будет выведено:

Результат: test1, test2

3 голосов
/ 25 августа 2009

Я думаю, что Эндрю Хэйр прав, вам нужно просто вернуть «объект». Что касается редакционных комментариев, я чувствую, что работа с необработанными объектами в ОО-коде может быть «запахом кода». В некоторых случаях это правильно, но в большинстве случаев лучше определить интерфейс для возврата или использовать некоторый тип базового класса, если вы собираетесь возвращать связанные типы.

3 голосов
/ 25 августа 2009

Нет, анонимные типы не могут существовать вне контекста, в котором они созданы, и в результате не могут использоваться как возвращаемый тип метода. Вы можете вернуть экземпляр как object, но для этой цели гораздо лучше явно создать свой собственный тип контейнера.

2 голосов
/ 15 июня 2018

Вы также можете инвертировать поток управления, если это возможно:

    public abstract class SafeAnon<TContext>
    {
        public static Anon<T> Create<T>(Func<T> anonFactory)
        {
            return new Anon<T>(anonFactory());
        }

        public abstract void Fire(TContext context);
        public class Anon<T> : SafeAnon<TContext>
        {
            private readonly T _out;

            public delegate void Delayed(TContext context, T anon);

            public Anon(T @out)
            {
                _out = @out;
            }

            public event Delayed UseMe;
            public override void Fire(TContext context)
            {
                UseMe?.Invoke(context, _out);
            }
        }
    }

    public static SafeAnon<SomeContext> Test()
    {
        var sa = SafeAnon<SomeContext>.Create(() => new { AnonStuff = "asdf123" });

        sa.UseMe += (ctx, anon) =>
        {
            ctx.Stuff.Add(anon.AnonStuff);
        };

        return sa;
    }

    public class SomeContext
    {
        public List<string> Stuff = new List<string>();
    }

, а затем и в другом месте:

    static void Main()
    {
        var anonWithoutContext = Test();

        var nowTheresMyContext = new SomeContext();
        anonWithoutContext.Fire(nowTheresMyContext);

        Console.WriteLine(nowTheresMyContext.Stuff[0]);

    }
2 голосов
/ 25 августа 2009

Нет, нет поддержки для расширения области действия анонимного класса вне метода. За пределами метода класс действительно анонимный, и отражение - единственный способ получить доступ к его членам.

...