En cuanto a la duda, como ya te han explicado en los comentarios, es más un tema estético que práctico.
En mi caso yo suelo preferir para esos casos propiedades autoencapsuladas:
public static string ValorGlobal { get; set; } = string.Empty;
pero es más una manía personal que una buena práctica.
Lo que sí me parece importante es que, una vez que decidas definir estos casos de una forma, lo hagas siempre de la misma forma. Especialmente si hay varias personas trabajando sobre el mismo proyecto. Pero, aunque seas tú el único que trabaja sobre él, a ti mismo te resultará más fácil leer tu propio código si siempre sigues las mismas convenciones.
En cuanto al uso de variables globales ya te explicaban en la otra pregunta que el hecho de que se desaconsejen no quiere decir que no puedan usarse nunca, lo que quiere decir es que normalmente se usan más de lo debido. En muchas ocasiones se utilizan por comodidad, y así ahorrarse trabajo de estructurar el código, y acaban dando muchos problemas. Uno de los motivos principales, como te indicaban, es que pierdes la visión de las dependencias internas de tu código. Y así llegamos a tu pregunta: ¿cómo solucionamos ese problema de mantener las dependencias del código a través de la "inyección de dependencias"?
Voy a ver si consigo aclararlo. No pretendo que esto sea una especie de tutorial sobre la inyección de dependencias, simplemente trataré de aclarar el concepto.
Pongamos que, como comentas, tenemos un formulario en cuyo código necesitamos el nombre del usuario conectado. Podríamos crear una variable global en la aplicación y acceder a ella desde el formulario. El problema es que la única forma de saber que el funcionamiento del formulario depende del usuario conectado es bucear en el código para ver si en algún lugar se accede a la variable global.
Lo que propone la inyección de dependencias es que el código del formulario no acceda a elementos externos, si no que estos sean "inyectados" dentro del formulario. Uno de los métodos más utilizados es la inyección a través de constructor que es el que te proponían. Consistiría en añadir un argumento al constructor para recibir el nombre de usuario y que sea el valor de este argumento el que se utilice en el código. Algo así:
public partial class Form1 : Form
{
public Form1(string nombreUsuario)
{
InitializeComponent();
Text = $@"Usuario: {nombreUsuario}";
}
}
De esta forma viendo el constructor ya ves las dependencias que tiene el elemento. Además es mucho más testeable: si quieres ver si el formulario funciona con diferentes usuarios no tendrías más que instanciarlo con diferentes nombres:
new Form1("Pepe");
new Form1("Susana");
new Form1("Juan");
Evidentemente esto implica que en todos los lugares en los que se instancia el formulario debería cambiarse la llamada de
new Form1();
a
new Form1(nombreUsuario);
Además, si en el código del formulario se necesita acceder a otro elemento externo, habría que volver a modificar el constructor y, por lo tanto, todos los lugares donde se creen instancias del formulario.
Para evitar este problema existen los "contenedores de dependencias". Los contenedores de dependencias son elementos en los que se registran las diferentes dependencias externas y se encarga de crear las instancias de los diferentes elementos inyectándoles las dependencias necesarias.
Para nuestro ejemplo podríamos crear un contenedor de dependencias sencillo:
public static class Contenedor
{
public static string NombreUsuario { get; set; }
public static T Crear<T>() where T : class
{
// Si tiene constructor sin parámetros lo usamos
var constructor = typeof(T).GetConstructor(new Type[]{});
if (constructor != null)
{
return (T) constructor.Invoke(new object[] { });
}
// Si tiene constructor con un argumento string lo usamos pasándole el
// valor de la propiedad NombreUsuario
constructor = typeof(T).GetConstructor(new[] {typeof(string)});
if (constructor != null)
{
return (T) constructor.Invoke(new object[] {NombreUsuario});
}
throw new Exception("No se ha encontrado un constructor válido");
}
}
El contenedor tiene una propiedad NombreUsuario
a través de la cual se le puede dar un valor a la dependencia nombre de usuario.
Tiene también un método genérico Crear
que se encarga de crear y devolver una nueva instancia de un tipo dado. Este método comprueba si el tipo tiene un constructor sin parámetros y, si es así, lo utiliza para crear la nueva instancia. Si no, comprueba si tiene un constructor con un único parámetro de tipo string
y, si es así, lo utiliza para crear la instancia pasándole como parámetro el nombre de usuario. Si no encuentra uno de estos constructores genera una excepción indicando que el tipo no tiene un constructor válido.
De esta forma en nuestra aplicación podríamos crear una nueva instancia del formulario utilizando el método Crear
del contenedor de dependencias, sustituyendo
new Form1(nombreUsuario);
por
Contenedor.Crear<Form1>();
De esta forma si en el futuro se añade una nueva dependencia a Form1
únicamente habría que modificar el método Crear
del contenedor para que sea capaz de inyectar esta nueva dependencia. Sin necesitar modificar todos los lugares donde se crean instancias del formulario.
Previamente deberemos haber establecido el valor de la dependencia "nombre de usuario" en el contenedor de dependencias:
Contenedor.NombreUsuario = UsuarioLogin.Nombre;
Por supuesto la implementación de este contenedor de dependencias puede ser una tarea muy compleja (aunque muy educativa también), pero existen contenedores de dependencias ya implementados que dan una gran funcionalidad y rendimiento como Autofac o Ninject.
Puedes ampliar información en este artículo en el que explico lo que es la inyección de dependencias utilizando ejemplos con Ninject:
Ninject. Hola Mundo (I). ¿Qué es la inyección de dependencias?
Espero que ayude.