Создайте универсальный класс с внутренним конструктором

Можно ли создать объект с его внутренним конструктором внутри универсального метода?

public abstract class FooBase { }

public class Foo : FooBase {
   internal Foo() { }
}

public static class FooFactory {
    public static TFooResult CreateFoo<TFooResult>()
    where TFooResult : FooBase, new() {
        return new TFooResult();
    }
}

FooFactory находится в той же сборке, что и Foo. Классы вызывают фабричный метод следующим образом:

var foo = FooFactory.CreateFoo<Foo>();

Они получают ошибку времени компиляции:

«Foo» должен быть неабстрактным типом с общедоступным конструктором без параметров, чтобы использовать его в качестве параметра «TFooType» в универсальном типе или методе «FooFactory.CreateFoo()».

Есть ли способ обойти это?

Я также пробовал:

Activator.CreateInstance<TFooResult>(); 

Это вызывает ту же ошибку во время выполнения.


person David Neale    schedule 06.09.2010    source источник
comment
Как ни странно, я не получаю никаких ошибок с вашим кодом в .NET 4. Не уверены, что это ограничение было ослаблено? Была ли это ошибка времени компиляции или времени выполнения?   -  person Paul Ruane    schedule 06.09.2010
comment
А, я использую .NET 3.5. Я покопаюсь. Это была ошибка времени компиляции.   -  person David Neale    schedule 06.09.2010
comment
В таком случае смотрите мой ответ.   -  person Paul Ruane    schedule 06.09.2010


Ответы (3)


Вы можете удалить ограничение new() и вернуться:

//uses overload with non-public set to true
(TFooResult) Activator.CreateInstance(typeof(TFooResult), true); 

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

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

person Ani    schedule 06.09.2010
comment
Я пробовал это - та же ошибка возникает только во время выполнения, а не при компиляции. - person David Neale; 06.09.2010
comment
@David Neale: с этим справится вызов перегрузки nonPublic. Обновлено. - person Ani; 06.09.2010
comment
Отлично - такой перегрузки я не видел. Спасибо. - person David Neale; 06.09.2010

Аргумент type должен иметь public конструктор без параметров. При использовании вместе с другими ограничениями ограничение new() должно указываться последним.

http://msdn.microsoft.com/en-us/library/d5x73970.aspx

редактировать: так что нет, если вы используете ограничение new(), вы не можете передать этот класс, если вы не используете ограничение new(), вы можете попробовать использовать отражение для создания нового экземпляра

public static TFooResult CreateFoo<TFooResult>()
where TFooResult : FooBase//, new()
        {
            return (TFooResult)typeof(TFooResult).GetConstructor(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new Type[] {}, null).Invoke(new object[]{});
            //return new TFooResult();
        }
person Kikaimaru    schedule 06.09.2010

Может быть несколько обходных путей, как показано ниже, но я не думаю, что вы хотите пойти по этому пути!

  • Поместите оператор switch внутри Factory, который создаст экземпляр на основе типа параметра типа.

  • Каждая конкретная реализация FooBase будет регистрироваться в FooFactory, передавая фабричный метод для собственного создания. Таким образом, FooFactory будет использовать внутренний словарь.

  • Расширение в аналогичной строке, за исключением сопоставления между параметром типа и конкретной реализацией, будет внешним кодом (файл xml, конфигурация и т. д.). Здесь также могут помочь контейнеры IOC/DI.

person VinayC    schedule 06.09.2010