Как вы принудительно создаете сигнатуры конструктора и статические методы?
Есть ли способ заставить (дочерний) класс иметь конструкторы с конкретными сигнатурами или конкретными статическими методами в С# или Java?
Вы не можете явно использовать интерфейсы для этого, и я знаю, что он будет ограниченным использованием. Один экземпляр, в котором я нахожу его полезным, - это когда вы хотите применить некоторые рекомендации по дизайну, например:
Исключения
Все они должны иметь четыре канонических конструктора, но нет никакого способа обеспечить их соблюдение. Вы должны полагаться на такой инструмент, как FxCop (случай С#), чтобы поймать их.
Операторы
Нет договора, который указывает, что два класса могут быть суммированы (с оператором + в С#)
Есть ли какой-либо шаблон проектирования, чтобы обойти это ограничение?
Какую конструкцию можно добавить к языку для преодоления этого ограничения в будущих версиях С# или Java?
Ответы
Ответ 1
Не соблюдается во время компиляции, но я потратил много времени на подобные проблемы; a библиотека с математическими возможностями с общим доступом, а эффективный (не по умолчанию) API ctor доступен в MiscUtil. Однако они проверяются только при первом использовании во время выполнения. На самом деле это не большая проблема. В ваших модульных тестах очень быстро найти отсутствующий оператор /ctor. Но он работает, и очень быстро...
Ответ 2
Используя generics, вы можете заставить аргумент типа иметь конструктор без параметров, но это о его пределе.
Кроме дженериков, было бы сложно использовать эти ограничения, даже если они существовали, но иногда это может быть полезно для параметров/аргументов типа. Разрешение статических элементов в интерфейсах (или, возможно, статических интерфейсах) также может помочь с проблемой "общего числового оператора".
I написал об этом некоторое время назад, столкнувшись с аналогичной проблемой.
Ответ 3
Вы можете использовать шаблон Factory.
interface Fruit{}
interface FruitFactory<F extends Fruit>{
F newFruit(String color,double weight);
Cocktail mixFruits(F f1,F f2);
}
Затем вы можете создавать классы для любого типа фруктов
class Apple implements Fruit{}
class AppleFactory implements FruitFactory<Apple>{
public Apple newFruit(String color, double weight){
// create an instance
}
public Cocktail mixFruits(Apple f1,Apple f2){
// implementation
}
}
Это не гарантирует, что вы не можете создавать экземпляр иным способом, чем с помощью Factory, но по крайней мере вы можете указать, какие методы вы запросите у Factory.
Ответ 4
Силовые конструкторы
Вы не можете. Самое близкое, что вы можете сделать, это сделать конструктор по умолчанию приватным, а затем предоставить конструктор с параметрами. Но у него все еще есть лазейки.
class Base
{
private Base() { }
public Base(int x) {}
}
class Derived : Base
{
//public Derived() { } won't compile because Base() is private
public Derived(int x) :base(x) {}
public Derived() : base (0) {} // still works because you are giving a value to base
}
Ответ 5
Проблема в языке заключается в том, что статические методы действительно являются гражданами второго сорта (конструктор также является своего рода статическим методом, потому что для начала вам не нужен экземпляр).
Статические методы - это просто глобальные методы с пространством имен, они на самом деле не "принадлежат" классу, в котором они определены (ОК, они имеют доступ к приватным (статическим) методам в классе, но об этом).
Проблема на уровне компилятора заключается в том, что без экземпляра класса у вас нет таблицы виртуальных функций, что означает, что вы не можете использовать все материалы наследования и полиморфизма.
Я думаю, что можно было бы сработать, добавив глобальную/статическую виртуальную таблицу для каждого класса, но если она еще не была выполнена, возможно, для этого есть веская причина.
Ответ 6
Вот я бы решил, если бы я был разработчиком языка.
Разрешить интерфейсы включать статические методы, операторы и конструкторы.
interface IFoo
{
IFoo(int gottaHaveThis);
static Bar();
}
interface ISummable
{
operator+(ISummable a, ISummable b);
}
Не разрешать соответствующие new IFoo(someInt)
или IFoo.Bar()
Разрешить наследование наследования (как и статические методы).
class Foo: IFoo
{
Foo(int gottaHaveThis) {};
static Bar() {};
}
class SonOfFoo: Foo
{
// SonOfFoo(int gottaHaveThis): base(gottaHaveThis); is implicitly defined
}
class DaughterOfFoo: Foo
{
DaughhterOfFoo (int gottaHaveThis) {};
}
Разрешить программисту прибегать к интерфейсам и проверять, если необходимо, во время выполнения, если трансляция семантически допустима, даже если класс явно не указывает.
ISummable PassedFirstGrade = (ISummable) 10;
Ответ 7
К сожалению, вы не можете в С#. Вот удар, хотя:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Foo.Instance.GetHelloWorld());
Console.ReadLine();
}
}
public class Foo : FooStaticContract<FooFactory>
{
public Foo() // Non-static ctor.
{
}
internal Foo(bool st) // Overloaded, parameter not used.
{
}
public override string GetHelloWorld()
{
return "Hello World";
}
}
public class FooFactory : IStaticContractFactory<Foo>
{
#region StaticContractFactory<Foo> Members
public Foo CreateInstance()
{
return new Foo(true); // Call static ctor.
}
#endregion
}
public interface IStaticContractFactory<T>
{
T CreateInstance();
}
public abstract class StaticContract<T, Factory>
where Factory : IStaticContractFactory<T>, new()
where T : class
{
private static Factory _factory = new Factory();
private static T _instance;
/// <summary>
/// Gets an instance of this class.
/// </summary>
public static T Instance
{
get
{
// Scary.
if (Interlocked.CompareExchange(ref _instance, null, null) == null)
{
T instance = _factory.CreateInstance();
Interlocked.CompareExchange(ref _instance, instance, null);
}
return _instance;
}
}
}
public abstract class FooStaticContract<Factory>
: StaticContract<Foo, Factory>
where Factory : IStaticContractFactory<Foo>, new()
{
public abstract string GetHelloWorld();
}
Ответ 8
Хорошо, я знаю из формулировки вашего вопроса, который вы ищете для принудительного исполнения во время компиляции. Если у кого-то нет блестящего предложения/взлома, который позволит вам сделать это так, как вы подразумеваете компилятор, я бы посоветовал вам написать специальную задачу MSbuild, которая сделала это. Рамка AOP, такая как PostSharp, может помочь вам выполнить это в кратчайшие сроки, опираясь на модель построения сборки.
Но что не так с анализом кода или принудительным исполнением? Может быть, это просто предпочтение, и я уважаю это, но у меня лично нет проблем с тем, чтобы CA/FXCop проверял эти вещи... и если вы действительно хотите заставить сторонних разработчиков ваших классов иметь подписи конструктора, вы всегда можете добавлять правила, проверка времени в конструкторе базового класса с использованием отражения.
Ричард
Ответ 9
Я не уверен, чего вы пытаетесь достичь, не могли бы вы рассказать? Единственная причина для принудительного использования конкретного конструктора или статического метода в разных классах - это попытаться выполнить их динамически во время выполнения, правильно ли это?
Конструктор предназначен для конкретного конкретного класса, поскольку он предназначен для инициализации конкретных потребностей класса. Насколько я понимаю, причина, по которой вы хотите что-то принудить в иерархии или интерфейсе классов, заключается в том, что это действие/операция, относящаяся к выполняемому процессу, но может варьироваться в разных обстоятельствах. Я считаю, что это предполагаемое преимущество полиморфизма, которого невозможно достичь с помощью статических методов.
Это также потребует знания определенного типа класса, который вы хотели бы назвать статическим методом, который разбил бы все полиморфное скрытие различий в поведении, которое пытается достичь интерфейс или абстрактный класс.
Если поведение, представленное конструктором, должно быть частью контракта между клиентом этих классов, я бы добавил его явно в интерфейс.
Если иерархия классов имеет аналогичные требования к инициализации, тогда я бы использовал абстрактный базовый класс, однако для наследующих классов должно быть до того, как они найдут параметр для этого конструктора, который может включать в себя отображение аналогичного или идентичного конструктора.
Если это предназначено для создания разных экземпляров во время выполнения, я бы рекомендовал использовать статический метод для абстрактного базового класса, который знает разные потребности всех конкретных классов (для этого вы можете использовать инъекцию зависимостей).