转换为匿名类型

gsh*_*arp 49 c# anonymous-types anonymous-class

我今天遇到了以下问题,我想知道我的问题是否有解决方案.

我的想法是构建匿名类并将其用作WinForm BindingSource的数据源:

public void Init()
{
    var option1 = new
                  {
                      Id = TemplateAction.Update,
                      Option = "Update the Templates",
                      Description = "Bla bla 1."
                  };

    var option2 = new
                  {
                      Id = TemplateAction.Download,
                      Option = "Download the Templates",
                      Description = "Bla bla 2."
                  };

    var list = new[] {option1, option2}.ToList();

    bsOptions.DataSource = list; // my BindingSource

    // cboTemplates is a ComboBox
    cboTemplates.DataSource = bsOptions; 
    cboTemplates.ValueMember = "Id";
    cboTemplates.DisplayMember = "Option";

    lblInfoTemplates.DataBindings.Add("Text", bsOptions, "Description");
}
Run Code Online (Sandbox Code Playgroud)

到目前为止工作正常.

我遇到的问题是从BindingSource的"Current"属性中获取Id,因为我无法将其强制转换为匿名类型:

private void cmdOK_Click(object sender, EventArgs e)
{
    var option = (???)bsOptions.Current;
}
Run Code Online (Sandbox Code Playgroud)

我想有没有办法找出"当前"的类型并访问"Id"属性?也许某人有一个好的解决方案......

我知道还有其他(也有更好的)方法来获取Id(反射,从ComboBox中读取值,而不是使用匿名tpyes,...)如果可以从bsOptions中获取Type,我只是很好.目前以优雅的方式.

ang*_*son 85

请注意,根据评论,我只想指出,当你需要像这样传递它时,我也建议使用真实类型.匿名类型应该一次只能在一个方法中本地使用(在我看来),但无论如何,这是我的答案的其余部分.


您可以使用技巧,通过欺骗编译器为您推断出正确的类型:

using System;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {
            var a = new { Id = 1, Name = "Bob" };
            TestMethod(a);

            Console.Out.WriteLine("Press enter to exit...");
            Console.In.ReadLine();
        }

        private static void TestMethod(Object x)
        {
            // This is a dummy value, just to get 'a' to be of the right type
            var a = new { Id = 0, Name = "" };
            a = Cast(a, x);
            Console.Out.WriteLine(a.Id + ": " + a.Name);
        }

        private static T Cast<T>(T typeHolder, Object x)
        {
            // typeHolder above is just for compiler magic
            // to infer the type to cast x to
            return (T)x;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

诀窍是在程序集内部,相同的匿名类型(相同的属性,相同的顺序)解析为相同的类型,这使得上面的技巧工作.

private static T CastTo<T>(this Object value, T targetType)
{
    // targetType above is just for compiler magic
    // to infer the type to cast value to
    return (T)value;
}
Run Code Online (Sandbox Code Playgroud)

用法:

var value = x.CastTo(a);
Run Code Online (Sandbox Code Playgroud)

但我们真的在这里推动极限.使用真实的类型,它看起来也感觉更干净.

  • 根据Mads Torgersen的说法,C#团队将这个技巧称为"逐个演示".请参阅本文的评论(第一篇):http://tomasp.net/blog/cannot-return-anonymous-type-from-method.aspx (5认同)
  • 每个人都在说"哦,那是邪恶的",但为什么呢?它只是运行时可检查的,但许多语言都是以这种方式工作的,就像Python一样.这可能是一个可接受的设计决定.无论如何,如果你从一个对象转换回任何东西,它只能运行时检查.那么,如果它是一个字符串或两个字符串,你重新投入的差异是什么? (5认同)
  • 完全同意,在这里我至少会在框架中使用Tuple或类似的预定义类型,但我可能会为此场景创建一个新类型. (2认同)
  • 我同意'不要被使用',现在有Tuple <,...>如果你真的懒得写一个小班. (2认同)

Mar*_*čič 18

而不是强制转换为自定义类型尝试使用动态类型.

您的事件处理程序看起来像这样:

private void cmdOK_Click(object sender, EventArgs e)
{
    dynamic option = bsOptions.Current;
    if (option.Id == 1) { doSomething(); }
      else { doSomethingElse(); }
}
Run Code Online (Sandbox Code Playgroud)

  • +1 - 这是C#4.0中的一个很好的选择.option.Id将在这种情况下在运行时进行评估. (2认同)

Vil*_*lx- 8

引用MSDN:

除对象外,匿名类型不能强制转换为任何接口或类型.


Phi*_*ert 6

在C#3.0中,这是不可能的.您将不得不等待C#4.0,它允许使用"动态"变量在运行时访问属性.