¿Cómo decide el sistema de resolución de sobrecarga de métodos qué método llamar cuando se pasa un valor nulo?
Entonces, por ejemplo, tienes un tipo como:
public class EffectOptions
{
public EffectOptions ( params object [ ] options ) {}
public EffectOptions ( IEnumerable<object> options ) {}
public EffectOptions ( string name ) {}
public EffectOptions ( object owner ) {}
public EffectOptions ( int count ) {}
public EffectOptions ( Point point ) {}
}
Aquí solo doy el ejemplo usando constructores, pero el resultado será el mismo si fueran métodos no constructores en el tipo en sí, ¿verdad?
Entonces cuando lo hagas:
EffectOptions options = new EffectOptions (null);
¿Qué constructor se llamaría y por qué?
Podría probar esto yo mismo, pero quiero entender cómo funciona el sistema de resolución de sobrecarga (no estoy seguro si así se llama).
Para conocer las reglas exactas, consulte la especificación de resolución de sobrecarga . Pero brevemente, es así.
Primero, haga una lista de todos los constructores accesibles .
public EffectOptions ( params object [ ] options )
public EffectOptions ( IEnumerable<object> options )
public EffectOptions ( string name )
public EffectOptions ( object owner )
public EffectOptions ( int count )
public EffectOptions ( Point point )
A continuación, elimine todos los constructores que no sean aplicables . Un constructor aplicable es aquel en el que cada parámetro formal tiene un argumento correspondiente, y el argumento es implícitamente convertible al tipo de parámetro formal. Suponiendo que Point es un tipo de valor, eliminamos las versiones "int" y "Point". Eso deja
public EffectOptions ( params object[] options )
public EffectOptions ( IEnumerable<object> options )
public EffectOptions ( string name )
public EffectOptions ( object owner )
Ahora, tenemos que considerar si el que tiene "params" es aplicable en su forma expandida o no expandida . En este caso es aplicable en ambas formas. Cuando eso sucede, descartamos la forma expandida . entonces eso deja
public EffectOptions ( object[] options )
public EffectOptions ( IEnumerable<object> options )
public EffectOptions ( string name )
public EffectOptions ( object owner )
Ahora debemos determinar cuál es el mejor de los candidatos aplicables. Las reglas de la mejor calidad son complicadas, pero la versión resumida es que más específico es mejor que menos específico . La jirafa es más específica que el mamífero, el mamífero es más específico que el animal, el animal es más específico que el objeto.
La object
versión es menos específica que todas ellas, por lo que se puede eliminar. La IEnumerable<object>
versión es menos específica que la object[]
versión (¿ves por qué?) por lo que también se puede eliminar. Eso deja
public EffectOptions ( object[] options )
public EffectOptions ( string name )
Y ahora estamos estancados. object[]
No es ni más ni menos específico que string
. Por tanto, esto da un error de ambigüedad.
Esto es sólo un breve esbozo; el algoritmo de desempate real es mucho más complicado. Pero esos son los conceptos básicos.
En este caso, el compilador de C# no seleccionará ningún constructor y, en su lugar, generará un error. El valor null
es legal para varios de los constructores disponibles y no hay suficiente lógica de desempate para elegir uno, por lo que se produce un error.
La lógica de resolución de sobrecarga del compilador de C# es un proceso complejo, pero a continuación se ofrece una breve (e inherentemente incompleta) descripción general de cómo funciona.
- Recoge a todos los miembros con el nombre dado
- Filtrar los miembros a aquellos con listas de parámetros que sean compatibles con los argumentos proporcionados y con la accesibilidad adecuada.
- Si los miembros restantes tienen más de un elemento, emplee la lógica de desempate para elegir al mejor de ellos.
Los detalles completos se enumeran en la sección 7.4 de la especificación del lenguaje C#. Y estoy seguro de que Eric llegará pronto para dar una descripción mucho más precisa :)