如何从匿名类型中获取属性的值?

28

我有一个由Linq查询填充的数据网格。当数据网格中聚焦的行改变时,我需要将一个变量设置为该对象中的某个属性。

我尝试了...

var selectedObject = view.GetRow(rowHandle);
_selectedId = selectedObject.Id;

...但是编译器对此并不关心(“嵌入语句不能是声明或带标签的语句”)。

看起来应该很容易访问属性。运行时检查对象显示了我期望的所有属性,我只是不知道如何访问它们。

我如何访问匿名对象的属性?

澄清编辑:

我恰好正在使用DevExpress XtraGrid控件。我使用了一个由几个不同对象组成的Linq查询来加载此控件,因此使得数据与我已经有的任何一个类不是真正符合的(即,我无法将其转换为任何东西)。

我正在使用.NET 3.5。

当我查看view.GetRow(rowHandle)方法的结果时,我会得到一个匿名类型,看起来像这样:

{ ClientId = 7, ClientName = "ACME Inc.", Jobs = 5 }

我的目标是从这个匿名类型中获取ClientId,以便我可以执行其他操作(例如在该客户记录中加载表单)。

我尝试了早期答案中的几个建议,但无法达到获取此ClientId的点。


“view” 这个标识符的确切类型是什么? - AnthonyWJones
"View" 是来自DevExpress的GridView控件。 - Sailing Judo
12个回答

62

你曾经尝试过使用反射吗?这是一个示例代码片段:

// use reflection to retrieve the values of the following anonymous type
var obj = new { ClientId = 7, ClientName = "ACME Inc.", Jobs = 5 }; 
System.Type type = obj.GetType(); 
int clientid = (int)type.GetProperty("ClientId").GetValue(obj, null);
string clientname = (string)type.GetProperty("ClientName").GetValue(obj, null);

// use the retrieved values for whatever you want...

谢谢Abdoul,你的代码非常易于理解。最近我遇到了一个匿名类型的问题,在这里:http://stackoverflow.com/questions/3534969/only-parameterless-constructors-and-initializers-are-supported-in-linq-to-entitie 我留下了这个问题。经过一些搜索和周末的工作,我来到了这个页面,真的让我很开心。 - Developer
这正是我喜欢 Stack Overflow 的原因。 - Bergkamp
真的太棒了!这就是我寻找了两周的答案!非常感谢! - KomalJariwala
@ogborstad 已完成。我回答时,JaredPar提供的答案最准确。这个答案是12个月后出现的,我同意它更准确。只用了我7年才注意到。 - Sailing Judo

23
为给定的键获取数据项值的通用解决方案
public static T GetValueFromAnonymousType<T>( object dataitem, string itemkey ) {
    System.Type type = dataitem.GetType();
    T itemvalue = (T)type.GetProperty(itemkey).GetValue(dataitem, null);
    return itemvalue;
}

例子:

var dataitem = /* Some value here */;
bool ismember = TypeUtils.GetValueFromAnonymousType<bool>(dataitem, "IsMember");

1
我发现这个解决方案是声明匿名类型的下一个最佳选择。谢谢。 - DoomerDGR8

17
匿名类型的问题之一是它们在函数之间的使用很困难。没有办法给匿名类型“命名”,因此很难在它们之间进行强制转换。这使得它们无法用作元数据中出现的任何用户定义类型表达式。
我无法确定您上面使用的是哪个API。但是,API不可能返回强类型的匿名类型,因此我猜测selectedObject的类型为object。 C#3.0及以下版本不支持动态访问,因此即使在运行时可用,您也将无法访问属性Id。
您需要执行以下操作之一才能解决此问题:
  • 使用反射来获取属性
  • 创建完整类型并使用该类型填充数据网格
  • 使用众多hacky匿名类型转换之一
编辑: 以下是一个关于如何进行hack匿名类型转换的示例。
public T AnonymousTypeCast<T>(object anonymous, T typeExpression) { 
  return (T)anonymous;
}

...
object obj = GetSomeAnonymousType();
var at = AnonymousTypeCast(obj, new { Name = String.Empty, Id = 0 });

这种方法之所以不太可靠,是因为它很容易被破坏。例如,在创建匿名类型的方法中,如果我在那里添加另一个属性,则上述代码将编译但在运行时失败。


“hacky匿名类型转换”是什么? - AnthonyWJones
@AnthonyWJones,我添加了一个例子,展示了一种不太正规的匿名类型转换。 - JaredPar
谢谢你的回答。这是一个非常好的和优雅的解决方案。 它只有一个缺点:如果匿名对象来自另一个程序集,它就无法工作。这就是为什么你永远不应该从方法中返回匿名对象的原因之一,但并不是每个人都知道这一点,所以我不得不处理一个不幸恶性循环的库。 - David Liebeherr

16
你可以使用动态类型在运行时访问匿名类型的属性,而不必使用反射。
var aType = new { id = 1, name = "Hello World!" };
//...
//...
dynamic x = aType;
Console.WriteLine(x.name); // Produces: Hello World!

在这里阅读有关动态类型的更多信息:http://msdn.microsoft.com/en-us/library/dd264736.aspx


1
如此简单而优雅,我感到羞愧,竟然没有想到它 :) - percebus

3
当我在处理传递匿名类型并尝试重新定义它们时,最终发现编写一个处理对象的包装器更容易。这里有一篇关于它的博客文章链接。 http://somewebguy.wordpress.com/2009/05/29/anonymous-types-round-two/ 最终,你的代码看起来会像这样。
//create an anonymous type
var something = new {  
  name = "Mark",  
  age = 50  
};  
AnonymousType type = new AnonymousType(something);

//then access values by their property name and type
type.With((string name, int age) => {  
  Console.Write("{0} :: {1}", name, age);  
}); 

//or just single values
int value = type.Get<int>("age");   

1
正如JaredPar所猜测的那样,GetRow()的返回类型是object。在使用DevExpress网格时,您可以像这样提取所需的值:
int clientId = (int)gridView.GetRowCellValue(rowHandle, "ClientId");

这种方法与之前描述的“hacky匿名类型转换”有类似的缺点:您需要一个魔术字符串来标识列,再加上从对象到整数的类型转换。


1

DevExpress的xtraGridView有一个方法类似于GetRowCellDisplayText(int rowHandle, GridColumn column)。使用这个方法,下面的代码将返回匿名类型中的id。

var _selectedId = view.GetRowCellDisplayText(rowHandle, "Id");

虽然这并没有回答“如何访问匿名对象的属性”的问题,但它仍然试图解决问题的根本原因。

我尝试了使用devXpress 11.1版本,并且我可以看到该问题最早是在将近2.5年前提出的。可能问题的作者已经找到了解决方法或者解决方案。然而,我仍然回答这个问题,以便能够帮助其他人。


0
您可以像这样循环遍历匿名类型的属性:
var obj = new {someValue = "hello", otherValue = "world"};
foreach (var propertyInfo in obj.GetType().GetProperties() {
    var name = propertyInfo.Name;
    var value = propertyInfo.GetValue(obj, index: null);
    ...
}

0
var result = ((dynamic)DataGridView.Rows[rowNum].DataBoundItem).value;

这是获取列表(DataGridView)属性值的最简单形式。 - Rei Salazar

0
希望这可以帮到你,我传入了一个接口列表,需要从中获取一个不同的列表。首先,我获取一个匿名类型列表,然后遍历它将其转换为我的对象列表。
private List<StockSymbolResult> GetDistinctSymbolList( List<ICommonFields> l )
            {
                var DistinctList = (
                        from a
                        in l
                        orderby a.Symbol
                        select new
                        {
                            a.Symbol,
                            a.StockID
                        } ).Distinct();

                StockSymbolResult ssr;
                List<StockSymbolResult> rl = new List<StockSymbolResult>();
                foreach ( var i in DistinctList )
                {
                                // Symbol is a string and StockID is an int.
                    ssr = new StockSymbolResult( i.Symbol, i.StockID );
                    rl.Add( ssr );
                }

                return rl;
            }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接