usar son qué programacion métodos metodos metodo los estáticos estaticos cuando clases c# java oop language-features language-design

c# - son - ¿Cómo fuerza firmas de constructor y métodos estáticos?



qué son los métodos estáticos (9)

¿Hay alguna forma de forzar a una clase (secundaria) a tener constructores con firmas particulares o métodos estáticos particulares en C # o Java?

Obviamente, no puede usar interfaces para esto, y sé que tendrá un uso limitado. Un ejemplo en el que lo encuentro útil es cuando desea aplicar algunas pautas de diseño, por ejemplo:

Excepciones
Todos deberían tener los cuatro constructores canónicos, pero no hay forma de aplicarlo. Tienes que confiar en una herramienta como FxCop (caso C #) para atraparlos.

Operadores
No hay contrato que especifique que se pueden sumar dos clases (con operador + en C #)

¿Hay algún patrón de diseño para evitar esta limitación? ¿Qué constructo podría agregarse al lenguaje para superar esta limitación en futuras versiones de C # o Java?


Constructores de fuerza

No puedes. Lo más cercano que puede venir es hacer que el constructor predeterminado sea privado y luego proporcionar un constructor que tenga parámetros. Pero todavía tiene lagunas.

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 }


Aquí es que lo resolvería si fuera un diseñador de idiomas.

Permitir que las interfaces incluyan métodos estáticos, operadores y constructores.

interface IFoo { IFoo(int gottaHaveThis); static Bar(); } interface ISummable { operator+(ISummable a, ISummable b); }

No permita el new IFoo(someInt) o IFoo.Bar()

Permitir que los constructores se hereden (al igual que los métodos estáticos).

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) {}; }

Permita que el programador lance a las interfaces y verifique, si es necesario, en el tiempo de ejecución si el elenco es semánticamente válido, incluso si la clase no especifica explícitamente.

ISummable PassedFirstGrade = (ISummable) 10;


Bueno, sé por la redacción de su pregunta que está buscando la aplicación en tiempo de compilación. A menos que otra persona tenga una sugerencia / truco brillante que le permita hacer esto de la manera que usted está implicando al compilador, le sugiero que pueda escribir una tarea MSbuild personalizada que lo haga. Un marco de trabajo AOP como PostSharp podría ayudarlo a lograr esto en un momento dado mediante el respaldo de su modelo de tareas de compilación.

Pero, ¿qué hay de malo con el análisis de código o la ejecución en tiempo de ejecución? Tal vez sea solo preferencia y respeto eso, pero personalmente no tengo problemas para que CA / FXCop compruebe estas cosas ... y si realmente quiere obligar a los implementadores de sus clases a tener firmas de constructores, siempre puede agregar reglas run- tiempo de comprobación en el constructor de la clase base usando reflexión.

Ricardo


El problema en el lenguaje es que los métodos estáticos son realmente ciudadanos de segunda clase (un constructor también es un tipo de método estático, porque no necesita una instancia para comenzar).

Los métodos estáticos son solo métodos globales con un espacio de nombres, realmente no "pertenecen" a la clase en la que están definidos (OK, tienen acceso a métodos privados (estáticos) en la clase, pero eso es todo).

El problema en el nivel del compilador es que sin una instancia de clase no tiene una tabla de funciones virtuales, lo que significa que no puede usar todas las cosas de herencia y polimorfismo.

Creo que uno podría hacerlo funcionar agregando una tabla virtual global / estática para cada clase, pero si aún no se ha hecho, probablemente haya una buena razón para ello.


Lamentablemente no se puede en C #. Aquí hay un golpe:

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(); }


No estoy seguro de lo que intentas lograr, ¿puedes explicarlo? La única razón para forzar un constructor específico o un método estático entre diferentes clases es intentar ejecutarlos dinámicamente en tiempo de ejecución, ¿es correcto?

Un constructor debe ser específico para una clase en particular, ya que está destinado a inicializar las necesidades específicas de la clase. Según tengo entendido, la razón por la que querría aplicar algo en una jerarquía o interfaz de clase es que se trata de una actividad / operación relevante para el proceso que se está realizando, pero puede variar en diferentes circunstancias. Creo que este es el beneficio previsto del polimorfismo, que no se puede lograr con métodos estáticos.

También requeriría conocer el tipo específico de la clase para la que desea llamar el método estático, que rompería toda la ocultación polimórfica de las diferencias de comportamiento que la interfaz o clase abstracta está tratando de lograr.

Si el comportamiento representado por el constructor está destinado a ser parte del contrato entre el cliente de estas clases, entonces lo agregaría explícitamente a la interfaz.

Si una jerarquía de clases tiene requisitos de inicialización similares, entonces utilizaría una clase base abstracta; sin embargo, correspondería a las clases heredadas cómo encuentran el parámetro para ese constructor, lo que puede incluir exponer un constructor similar o idéntico.

Si esto tiene la intención de permitirle crear instancias diferentes en el tiempo de ejecución, le recomendaría usar un método estático en una clase base abstracta que conozca las diferentes necesidades de todas las clases concretas (podría usar inyección de dependencia para esto).


No se aplica en tiempo de compilación, pero he pasado mucho tiempo mirando problemas similares; una biblioteca matemática habilitada genéricamente y una API ctor eficiente (no predeterminada) están disponibles en MiscUtil . Sin embargo, estos solo se verifican al primer uso en tiempo de ejecución. En realidad, este no es un gran problema: las pruebas de su unidad deben encontrar cualquier operador / ctor faltante muy rápidamente. Pero funciona, y muy rápido ...


Podría usar el patrón de fábrica.

interface Fruit{} interface FruitFactory<F extends Fruit>{ F newFruit(String color,double weight); Cocktail mixFruits(F f1,F f2); }

A continuación, podría crear clases para cualquier tipo de fruta

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 } }

Esto no significa que no puede crear una instancia de otra manera que no sea usar la fábrica, pero al menos puede especificar qué métodos solicitaría de una fábrica.


Usando genéricos puede forzar un argumento de tipo para tener un constructor sin parámetros, pero eso es aproximadamente el límite.

Aparte de genéricos, sería complicado usar estas restricciones incluso si existieran, pero a veces podría ser útil para parámetros / argumentos de tipo. Permitir miembros estáticos en interfaces (o posiblemente interfaces estáticas) también podría ayudar con el problema del "operador numérico genérico".

Escribí sobre esto hace un momento cuando enfrentaba un problema similar.