Несколько универсальных типов в одном контейнере - PullRequest
1 голос
/ 22 мая 2010

Я искал ответ на этот вопрос относительно нескольких универсальных типов в одном контейнере, и я не могу заставить его работать: свойства класса Metadata не видны, так как абстрактный класс не имеет их. Вот немного измененная версия кода в оригинальном вопросе:

public abstract class Metadata
{
}

public class Metadata<T> : Metadata
{
    // Per Ben Voigt's comments, here are the rest of the properties:
    public NUM_PARAMS NumParams { get; set; }
    public FUNCTION_NAME Name { get; set; }
    public List<Type> ParamTypes { get; set; }
    public Type ReturnType { get; set; }
    //...C
    public T Function { get; set; }
    public Metadata(T function)
    {
        Function = function;
    }
}

List<Metadata> metadataObjects;
metadataObjects.Add(new Metadata<Func<double,double>>(SomeFunction));
metadataObjects.Add(new Metadata<Func<int,double>>(SomeOtherFunction));
metadataObjects.Add(new Metadata<Func<double,int>>(AnotherFunction));

foreach( Metadata md in metadataObjects)
{
      var tmp = md.Function; // <-- Error: does not contain a definition for Function
}

Точная ошибка:

ошибка CS1061: «Метаданные» не содержат определение для «функции» и нет метод расширения «Функция», принимающий первый аргумент типа «Метаданные» может быть найден (вы пропускаете используя директиву или сборку ссылка?)

Я полагаю, это потому, что абстрактный класс не определяет свойство Function, поэтому все усилия совершенно бесполезны. Есть ли способ, которым мы можем получить свойства?

Обновление

Основная идея заключается в том, что у меня есть генетическая программа, которая использует Metadata функций (или MetaFunction s) для построения деревьев выражений с этими функциями. Метаданные позволяют мне корректно сопоставить возврат одной функции с входными параметрами другой функции ... в основном мои функции превращаются в legos, и компьютер может комбинировать их различными способами. Все функции находятся в одном и том же «домене», поэтому у меня не возникнет проблем со случайным смешиванием и сопоставлением их.

Я храню Metadata или MetaFunction s в нескольких словарях:

  • в качестве ключа указывается имя функции.
  • другой имеет число параметров в качестве ключа.

В любом случае, я просто пытался придерживаться как можно более близкого к исходному вопросу ... основная проблема одна и та же, независимо от того, использую я List или Dictionary. Я также застрял с .NET 3.5 и не смогу некоторое время обновиться до .NET 4.0.

Ответы [ 3 ]

2 голосов
/ 22 мая 2010

Что бы вы сделали с md.Function, если бы могли прочитать? Вы не можете назвать это, потому что вы не знаете типы параметров. С C # 4.0 вы можете использовать dynamic, например, foreach (dynamic md in metadataObjects) и тогда вам не нужен Metadata абстрактный базовый класс. Если вы просто хотите получить доступ к членам Delegate, вы можете изменить абстрактный базовый класс на интерфейс, который имеет свойство Delegate Metadata { get; }, и явно реализовать его в Metadata<T>, тогда вы можете получить доступ, например, к. имя функции.

1 голос
/ 23 мая 2010

Я думаю, что главная проблема здесь в том, что вы пытаетесь решить очень Динамическую проблему с очень Статическими (но гибкими) инструментами Общего программирования. Итак, я вижу два пути для вас.

  1. Разделите все свои коллекции по границам типов, создавая разные коллекции для каждого типа функций, которые у вас есть. Это должно быть возможно в вашем случае, потому что вы знаете все типы заранее, чтобы вы знали, какие типы создавать.
  2. Примите динамический характер проблемы, которую вы пытаетесь решить, и затем используйте правильные инструменты для работы. Из того, что я могу сказать, вы хотите иметь возможность хранить список «функций», а затем динамически выбирать во время выполнения, какие из них вызывать с какими аргументами. В этом случае вам просто нужна лучшая модель.

Я бы выбрал вариант 2. Из моего понимания я думаю, что это была бы лучшая модель.

public class Variable
{
    public Type Type {get; protected set;}
    public Object Value {get;protected set;}
    public Variable(Object val)
    {
        Type = val.GetType();
        Value = val;
    }
    public Variable(Type t, Object val)
    {
        Type = t;
        Value = val;
    }
}

public class ComposableFunction
{
    public NUM_PARAMS NumParams { get; protected set; }
    public FUNCTION_NAME Name { get; protected set; }

    //our function signature
    public List<Type> ParamTypes { get; protected set; }
    public Type ReturnType { get; protected set; }

    private Delegate Function { get; set; }
    public Metadata (Delegate function)
    {
        Function = function;
    }
    public bool CanCallWith(params Variable vars)
    {
        return CanCallWith(vars);
    }
    public bool CanCallWith(IEnumerable<Variable> vars)
    {
        using(var var_enum = vars.GetEnumerator())
        using(var sig_enum = ParamTypes.GetEnumerator())
        {
            bool more_vars = false;
            bool more_sig =false;
            while(   (more_sig = sig_enum.MoveNext()) 
                  && (more_vars = var_enum.MoveNext())
                  && sig_enum.Current.IsAssignableFrom(var_enum.Current.Type));
            if(more_sig || more_vars)
                return false;
        }
        return true;
    }

    public Variable Invoke(params Variable vars)
    {
        return Invoke(vars);
    }
    public Variable Invoke(IEnumerable<Variable> vars)
    {
        return new Variable(ReturnType, Function.DynamicInvoke(vars.Select(v => v.Value)));
    }
}

Итак, теперь у нас есть хорошая модель, которая должна отвечать вашим требованиям, и, поскольку она не принимает параметров универсального типа, вы должны иметь доступ ко всем ее функциям, когда вы выполняете итерацию по List<ComposableFunction> или как угодно.

0 голосов
/ 22 мая 2010

вы правы, ошибка в том, что список считает, что у него есть куча объектов метаданных, поэтому при его повторении вы получаете ссылки на метаданные, чтобы получить доступ к свойству, определенному в подклассе, вам нужно убедиться, что объект на самом деле является этим подклассом, а затем выполняет приведение.

foreach( Metadata md in metadataObjects)
{
      var tmp =((Metadata<Func<double,double>>)md).Function; // but this will obviously fail if the type is incorrect. 
}

так что здесь вы просто торгуете определенной ошибкой времени компиляции за потенциальную ошибку времени выполнения (в зависимости от того, что в вашем списке). Реальный вопрос: что вы хотите делать со всеми этими различными обертками делегатов функций? какой тип переменной tmp вы ожидаете?

Вы также можете попробовать решение для тестирования типа, подобное этому

foreach( Metadata md in metadataObjects)
{
    var dd_md = md as Metadata<Func<double,double>>;
    var id_md = md as Metadata<Func<int,double>>;
    var di_md = md as Metadata<Func<double,int>>;
    if(dd_md != null)
    {
       var tmp1 =dd_md.Function;
    }
    else if(id_md != null)
    {
       var tmp2 =id_md.Function;
    }
    else if(di_md != null)
    {
       var tmp3 =di_md.Function;
    }
    //etc....

}

это также может быть жизнеспособным решением, если вы точно знаете, какие типы будут присутствовать раньше времени, но это раздражает и подвержено ошибкам.

...