【问题标题】:How to access anonymous method from generic list?如何从通用列表访问匿名方法?
【发布时间】:2015-04-28 22:08:56
【问题描述】:

我一直在开发一个使用Faker.NET 生成假数据的库。我遇到的问题是我不知道如何访问我传递给 DataGenerator 子类的构造函数的匿名方法。

问题是,为了创建泛型列表,我必须创建基类DataGenerator,但我无法拉起我的Func<T> 成员,因为该基类不是泛型,所以没有Tavailable。但是,我的 DataGenerator<T> 类确实公开了我的匿名方法 Generator 属性,但在迭代数据生成器列表时我还没有找到访问它的方法。

我们将不胜感激任何建议。

这是我目前所拥有的:

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Guid EmpUid { get; set; }
}

// Define other methods and classes here
public abstract class DataGenerator
{
    public abstract int GetWeight(string matchingProperty);
    public abstract Type Type { get;}
}

public abstract class DataGenerator<T> : DataGenerator
{
    public readonly string[] Tags;
    public readonly Func<T> Generator; 
    protected DataGenerator(Func<T> generator, params string[] tags)
    {
        Tags = tags;
        //How to access this?
        Generator = generator;
    }

    public override int GetWeight(string matchingProperty)
    {
        int sum = (from tag in Tags
            where matchingProperty.ToLowerInvariant().Contains(tag.ToLowerInvariant())
            select 1).Sum();
        return sum;
    }

    public override Type Type {
        get { return typeof(T); }
    }
}

public class StringDataGenerator : DataGenerator<string>
{
    public StringDataGenerator(Func<string> generator, params string[] tags) : base(generator, tags)
    {
    }
}

public class GuidDataGenerator : DataGenerator<Guid>
{
    public GuidDataGenerator(Func<Guid> generator, params string[] tags)
        : base(generator, tags)
    {
    }
}

我正在这里测试它:

private static void Main(string[] args)
    {
        var dataGeneratorList = new List<DataGenerator>
        {
            new StringDataGenerator(Name.First, "first", "name"),
            new StringDataGenerator(Name.Last, "last", "name"),
            new GuidDataGenerator(Guid.NewGuid, "uid", "id")
        };

        var writeProperties = typeof (Employee).GetProperties().Where(p => p.CanWrite);
        foreach (var property in writeProperties)
        {
            foreach (var dataGenerator in dataGeneratorList)
            {
                if (property.PropertyType == dataGenerator.Type)
                {
                    var weigth = dataGenerator.GetWeight(property.Name);
                    //How to access generator here???
                    var testValue = dataGenerator.Generator.Invoke();
                }
            }
        }
    }

【问题讨论】:

  • 为什么不在你的基类中添加一个以object 为抽象的重载,并通过转换为T 来实现它?
  • 我想我之前尝试过@AndreasNiedermair,但由于某种原因我无法让它工作
  • 在基类中添加抽象 object 重载不起作用的原因是,在我的情况下 Guid.NewGuid 返回一个 Guid,它不是对象而是 struct,它没有t 继承自 System.Object
  • 但是拳击可以救你...

标签: c# generics reflection


【解决方案1】:

当您标记时,鉴于您当前的设置,反射可能是您唯一的选择。

var func = dataGenerator.GetType().GetField("Generator").GetValue(dataGenerator);
var testValue = func.GetType().GetMethod("Invoke").Invoke(func, null);

我不确定任何人都可以称其为超级好,而且它不会超级快,但我想它可能足以满足您需要假数据的任何事情。

为了更好的衡量标准,here's it in action


您的问题实际上比表面上看起来要复杂一些。如果您只在 object 表单中使用它,一个很好的处理方法就是将抽象的 Generate 方法添加到基本的非泛型类:

public abstract object Generate();

然后在你的泛型中覆盖它:

public override object Generate()
{
    return this.Generator();
}

当然,这会返回一个object,这在泛型类中并不好。但至少它避免了反射。

避免这种反射废话的另一个解决方案可能是使用协方差,但不幸的是,break for value types, like Guid

public interface IDataGenerator<out T>
{
    int GetWeight(string matchingProperty);
    Type Type { get;}
    T Generate();
}

public abstract class DataGenerator<T> : IDataGenerator<T>
{
    public readonly string[] Tags;
    public readonly Func<T> Generator; 
    protected DataGenerator(Func<T> generator, params string[] tags)
    {
        Tags = tags;
        //How to access this?
        Generator = generator;
    }

    public T Generate(){
        return this.Generator();
    }

    . . .
}

然后变成一个更可取的,

private static void Main(string[] args)
{
    var dataGeneratorList = new List<IDataGenerator<object>>
    {
        new StringDataGenerator(Name.First, "first", "name"),
        new StringDataGenerator(Name.Last, "last", "name")

//            But this line doesn't work
//            new GuidDataGenerator(Guid.NewGuid, "uid", "id")
    };

    var writeProperties = typeof (Employee).GetProperties().Where(p => p.CanWrite);
    foreach (var property in writeProperties)
    {
        foreach (var dataGenerator in dataGeneratorList)
        {
            if (property.PropertyType == dataGenerator.Type)
            {
                var weigth = dataGenerator.GetWeight(property.Name);

                var testValue = dataGenerator.Generate();
            }
        }
    }
}

【讨论】:

  • 谢谢@MathewHaugen。有趣的解决方案。我认为您还得出结论,Guids 让我无法想出一个优雅的解决方案。到目前为止,反射技巧似乎正在发挥作用。我会等待,看看是否有更多建议。
  • @AdolfoPerez Guid 仅在使用协方差时是一个问题。它们适用于第二种方法,方法返回object。原则上我唯一的问题是,当我真正知道它将是什么类型(T)时,返回object 感觉很脏。但它有效。
  • 我认为您的第二个选项最适合我的需要。标记为已回答。感谢您的帮助!
猜你喜欢
  • 2013-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多