繁体   English   中英

如何将对象转换为不同程序集中的匿名类型?

[英]How to cast a object to an anonymous type in a different assembly?

我有一个GridView ,我的DataSource是:

items.Select(i => new { ID = i.ID, Foo = i }).ToList();

RowDataBound我想访问该对象,但我不知道如何转换它......

grid.RowDataBound += (s, e) =>
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        dynamic item = e.Row.DataItem as 'what?';
    }
};

如何访问此对象属性?


重现步骤

  1. 创建网站
  2. 创建一个页面(default.aspx)并放置一个<asp:GridView ID="grid" runat="server" />
  3. 在后面的代码中:

添加

protected void Page_Load(object sender, EventArgs ev)
{
    var provider = new FooProvider();

    grid.DataSource = provider.Elements;
    grid.RowDataBound += (s, e) =>
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            dynamic item = e.Row.DataItem;

            var test = item.ID;
        }
    };
    grid.DataBind();
}
  1. 创建类库项目
  2. 添加文件FooProvider.cs

代码:

public class FooProvider
{
    public IEnumerable<dynamic> Elements
    {
        get
        {
            return new int[] { 1, 2, 3 }
                .Select(i => new { ID = i, Foo = i * 3 }).ToList();
        }
    }
}

然后在网站上添加参考。


预期结果:获取匿名对象的 ID。

当前结果:

用户代码未处理 RuntimeBinderException

“object”不包含“ID”的定义


反思是唯一的方法吗?

不要使用匿名类型。 做一堂课。

public class MyCustomRow
{
  int ID {get;set;}
  Foo Foo {get;set;}
}

(wtf?动态的?)

如果您使用的是dynamic ,则根本不需要投射它。 只需转换您知道类型的值。

dynamic item = e.Row.DataItem;
DoSomething((int)item.Foo)

(尽管我同意 David B 的观点,即我宁愿拥有一个类型安全的声明类。)

顺便说一句,有一个技巧可以用于泛型,它可以让你做这样的事情:

var item = e.Row.DataItem.CastAnonymous(new {ID = 1, Foo = 1});
DoSomething(item.Foo);

...但我认为这是三个选项中最糟糕的一个。

编辑

跨程序集工作时,反射是完成您所要求的唯一方法。 匿名类型总是旨在保持在单个方法的范围内——这就是为什么不能将它们声明为参数或返回类型的原因。 它们使 LINQ 语句之类的事情变得不那么乏味,但它们的目的不是让 C# 成为一种脚本语言。 您是否有理由坚决反对声明强类型?

编辑 2

几年过去了,我们已经有了一些无需使用匿名类型的轻量级类型选项。

记录允许您使用一行代码进行类声明:

public record FooElement(int ID, int Foo);
public IEnumerable<FooElement> Elements
{
    get
    {
        return new int[] { 1, 2, 3 }
            .Select(i => new FooElement(i, i * 3)).ToList();
    }
}
var item = (FooElement) e.Row.DataItem;
var test = item.ID;

如果你真的不想创建一个命名类来表示你的返回类型,你可以使用 ValueTuple 语法:

public IEnumerable<(int ID, int Foo)> Elements
{
    get
    {
        return new int[] { 1, 2, 3 }
            .Select(i => (i, i * 3)).ToList();
    }
}
var item = ((int ID, int Foo)) e.Row.DataItem;
var test = item.ID;

或者

var (test, _) = ((int, int)) e.Row.DataItem;

为什么不使用 DataBinder.Eval() ?

    grid.RowDataBound += (s, e) =>
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            var test = DataBinder.Eval(e.Row.DataItem, "Id");
        }
    }

var item = e.Row.DataItem; 不工作?

你不能创建一个自定义模型类来存储信息以便你可以转换它吗?

我不认为你可以。 不久前我也遇到过类似的事情,我记得我不得不使用 <% %> 表示法来引用匿名属性(在你的例子中是 ID 和 Foo)。 如果您在 RowDataBound 中有更复杂的处理要做,则必须定义一个类。

您可以使用开源Impromptu-Interface框架将匿名类型强制转换为静态接口,使其可以在另一个程序集中工作。

Impromptu 接口实际上使用与 dynamic 关键字相同的 api,只是它将上下文设置为您在其中声明匿名类型的框架。这样动态调用将正确解析(因为匿名类型被编译为internal )。

return new int[] { 1, 2, 3 }
            .Select(i => new { ID = i, Foo = i * 3 }
                            .ActLike<IMyMadeUpInterface>()).ToList();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM