Подтвердить что ты не робот

Как вы принудительно создаете сигнатуры конструктора и статические методы?

Есть ли способ заставить (дочерний) класс иметь конструкторы с конкретными сигнатурами или конкретными статическими методами в С# или Java?

Вы не можете явно использовать интерфейсы для этого, и я знаю, что он будет ограниченным использованием. Один экземпляр, в котором я нахожу его полезным, - это когда вы хотите применить некоторые рекомендации по дизайну, например:

Исключения
Все они должны иметь четыре канонических конструктора, но нет никакого способа обеспечить их соблюдение. Вы должны полагаться на такой инструмент, как FxCop (случай С#), чтобы поймать их.

Операторы
Нет договора, который указывает, что два класса могут быть суммированы (с оператором + в С#)

Есть ли какой-либо шаблон проектирования, чтобы обойти это ограничение? Какую конструкцию можно добавить к языку для преодоления этого ограничения в будущих версиях С# или Java?

4b9b3361

Ответ 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

Я не уверен, чего вы пытаетесь достичь, не могли бы вы рассказать? Единственная причина для принудительного использования конкретного конструктора или статического метода в разных классах - это попытаться выполнить их динамически во время выполнения, правильно ли это?

Конструктор предназначен для конкретного конкретного класса, поскольку он предназначен для инициализации конкретных потребностей класса. Насколько я понимаю, причина, по которой вы хотите что-то принудить в иерархии или интерфейсе классов, заключается в том, что это действие/операция, относящаяся к выполняемому процессу, но может варьироваться в разных обстоятельствах. Я считаю, что это предполагаемое преимущество полиморфизма, которого невозможно достичь с помощью статических методов.

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

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

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

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