选择新对象作为参数,同时保留其运行时生成的类型

时间:2010-08-11 08:15:13

标签: c# linq generics

请考虑以下事项:

// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
    Id = Convert.ToInt32(row["Id"]),
    Name = row["Name"].ToString(),
    Email = row["Email"].ToString()
});

// or create a new object
var subset = new {
    Id = 1,
    Name = "something random",
    Email = "name@domain.tld"
};

有没有办法将 subset 变量用作方法的参数,而不将其转换为普通Object?你能以某种方式携带自动生成的变量类型吗?

每次我想将LINQ子集传递给方法时,我都试图避免创建新类。

欢迎使用随机通用方法。

5 个答案:

答案 0 :(得分:2)

不,传递匿名类型通常不是一个好主意,因为你丢失了类型信息*。您应该创建一个具体类型并使用它。

var subset = DataTable.Where(...).Select(row => new SomeType
{
    Id = Convert.ToInt32(row["Id"]),
    Name = row["Name"].ToString(),
    Email = row["Email"].ToString()
});

或者,如果您使用的是.NET 4,则可以使用Tuple类型。这是创建“一次性”类型的简单方法,并且仍然可以某些类型安全。


*实际上there is a workaround,但我认为这是一个丑陋的黑客,并建议你不要这样做。

答案 1 :(得分:2)

如果我需要这样做,我使用resharper的“用命名类替换匿名类型”重构选项。然后,您有一个适当的名为类型,以通过API公开,您不必做任何工作。这也为您提供了创建不可变(如匿名类型)或可变,嵌套与顶级等的选项。

顺便说一句,我不建议struct在这里(来自问题)。

另一种选择是将行为传递给方法 - 即Action<int,string,string> callback - 然后执行以下操作:

foreach(item in query) callback(item);

但是,我不喜欢这样,因为在以下情况下可能出现错误并不明显:

DoSomething(args, (id, email, name) => Email(To: email, Subject: name));

(错误是它应该可能(id, name, email),如果你明白我的意思了)

答案 2 :(得分:1)

您可以使用通用方法:

public static void Foo<T>(T item)
{
    // Do whatever
}

然后,如果你打电话

Foo(subset);

编译器会为您推断T。是否真的帮助你是另一回事......这取决于方法的意图。显然,Foo无法引用IdNameEmail等。

通常,如果多个方法应该知道相同的成员,那么您应该使用命名类型。将它们传递给泛型方法的通常情况是,该方法实际上并不关心涉及的类型,例如在LINQ中。

我已经为C#5发出了一个功能请求,我们应该能够创建具有与匿名类型(不变性,相等性,哈希码生成,ToString转储)相同功能的类型,但是对于简单命名类型。我们会看看它是否真的发生了......

答案 3 :(得分:0)

匿名类型在创建它们的上下文之外没有提供太多帮助。

如果您需要将匿名类型传递给方法,则此方法非常通用,如(示例)

void PrintAllObjectProperties(object obj);

巫婆你会用反射来做这项工作,或者你做错了什么。

答案 4 :(得分:0)

这就是我想出来的......


对象的扩展方法:

public static class ObjectExtensions
{
    /// <summary>
    /// Cast Object to anonymous type.
    /// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
    /// </summary>
    public static T ToAnonymousType<T>(this Object o, T t)
    {
        return (T)o;
    }
}

用法:

public void HandleAnonymousTypeAsParameter(Object o)
{
    var anonymousType = o.ToAnonymousType(new
    {
        Id = new Int32(),
        Foo = new String(),
        Bar = new String()
    });


    // ... You can do this in even less characters:
    var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}


HandleAnonymousTypeAsParameter(new
{
    Id = 1,
    Foo = "foo",
    Bar = "bar"
});


积分归John Skeet和Thomas P.