c# - español - Error de tiempo de compilación con LINQ Select en IEnumerable<dynamic>
static dynamic c# (4)
Bueno, viendo cómo el informe de errores se ha resuelto por bastante tiempo, resumámoslo:
Era un error, el compilador no aplicó la bandera dynamic
como debería, lo que provocó que se convirtiera en un object
. El error ha sido reparado. No sé cuándo estará disponible en VS2015, tal vez alguien más pueda proporcionar esa información.
Esto probablemente desencadenó algunas peculiaridades en el mecanismo de resolución de sobrecarga, dando como resultado mensajes de error engañosos y contenido de información sobre herramientas.
Por favor, mira más abajo para una actualización importante!
Tengo un código como este:
void Test(IEnumerable x)
{
var dynX = x.Cast<dynamic>();
var result = dynX.Select(_ => _.Text);
}
en un proyecto de biblioteca existente dirigido a .NET 4.5. IntelliSense de VS2015 subraya la parte de Text
, quejándose de que: ''objeto'' no contiene una definición para ''Texto'' ...
Efectivamente, la compilación falla con
error CS1061: ''objeto'' no contiene una definición para ''Texto'' y no se puede encontrar ningún método de extensión ''Texto'' que acepte un primer argumento de tipo ''objeto'' (¿falta una directiva using o una referencia de ensamblado?)
Este mensaje siempre dice ''object''
, incluso cuando cambio el molde a .Cast<IAsyncResult>()
o lo que sea. Cuando desplazo el parámetro lambda, la información sobre herramientas muestra que es del tipo IColumn
(que existe pero no está relacionado). De nuevo, no importa a qué tipo me dirija.
Sin embargo, cuando desplazo el método Select()
, muestra correctamente el parámetro como Func<dynamic, dynamic>
. Si especifico el tipo de parámetro lambda explícitamente, se compila. Si especifico los parámetros de tipo en Select()
explícitamente, también funciona.
Otros usos de LINQ con dynamic
están funcionando. Cuando copio este método a otro proyecto (existente) en la solución, también se compila. Cuando lo copio en otro archivo en el mismo proyecto, no se compila.
Se compila con VS2013, también.
El mismo error aparece para todos mis colegas, tanto en Windows 8.1 como en Windows 10.
Tal vez este es un problema extraño con la inferencia tipo ...?
Cosas que he probado que no ayudaron:
- Cree un nuevo proyecto de biblioteca .NET 4.5 y vuelva a agregar archivos y referencias faltantes
- Compare (original) los archivos del proyecto, sin diferencias, excepto el orden de los elementos
Actualizar
Bueno, logré crear un ejemplo de fallo mínimo autónomo:
static class Program
{
static void Main(string[] args)
{
IEnumerable x = new object[0];
IEnumerable<dynamic> dynX = x.Cast<dynamic>();
// CS1061 ''object'' does not contain a definition for ''Text''...
// var tooltip shows IColumn instead of IEnumerable<dynamic>
var result = dynX.Select(_ => _.Text);
}
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn { }
Desde mi punto de vista, esto indica claramente que hay un error grave en cómo VS2015 / la nueva versión del compilador resuelve los métodos de extensión.
Lo siguiente solo está relacionado de forma vaga y se trata principalmente de mensajes de error engañosos. Decidí dejarlo para que los comentarios no fueran confusos.
Peor aún, estos también fallan con el mismo error, aunque ni IEnumerable
ni object
posiblemente tengan un método de extensión Select()
:
// CS1061 ''object'' does not contain a definition for ''Text''
// var tooltip shows IColumn
var result2 = x.Select(_ => _.Text);
object o = new object();
// CS1061 ''object'' does not contain a definition for ''Text''
// var tooltip shows IColumn
var result3 = o.Select(_ => _.Text);
Apéndice
Este problema ahora se rastrea en el rastreador de errores Roslyn .
Este es simplemente un caso donde c # usa una forma de pato-tipado para permitir que LINQ funcione para cualquier tipo.
Comenzaré con un ejemplo simple.
Si defino una línea de clase Foo
esto:
public class Foo
{
public int Bar;
public int Select(Func<Foo, int> map)
{
return map(this);
}
}
Entonces puedo escribir este código:
Foo foo = new Foo() { Bar = 42 };
int query =
from f in foo
select f.Bar;
Obtengo una versión de LINQ que funciona en el tipo Foo
(no IEnumerable<T>
) que devuelve un int
(no IEnumerable<R>
).
Todos los operadores LINQ se pueden definir específicamente de esta manera.
El ejemplo anterior también se puede escribir así:
public class Foo
{
public int Bar;
}
public static class Ex
{
public static int Select(this Foo source, Func<Foo, int> selector)
{
return selector(source);
}
}
Ahora comienza a parecerse a tu código.
Entonces, si hacemos este cambio:
public class Foo
{
}
public static class Ex
{
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn { }
Entonces este código falla con el mismo error que el tuyo:
IEnumerable<dynamic> foo = new [] { new Foo() };
var query =
from f in foo
select f.Text;
Si hago este cambio:
public static class Ex
{
public static IColumn Select<TSource, TResult>(this IColumn source, Func<TSource, TResult> selector)
{
throw new NotImplementedException();
}
}
El código ahora informa que "RuntimeBinderException: ''Foo'' no contiene una definición para ''Texto''".
Lo que finalmente hemos hecho es mostrar que el compilador intenta implementar el operador LINQ Select
con su método Select
. Luego intenta ver si puede usar el método Select
definido como una sobrecarga para los tipos presentados y dado que la dynamic
se puede "lanzar" a IColumn
, indica que su método Select
es la mejor sobrecarga. Además, dado que el compilador solo buscará los candidatos en la clase actual, no buscará el operador LINQ estándar.
Por supuesto, entonces el compilador, usando su sobrecarga, lanza dynamic
al object
y luego trata de encontrar la propiedad .Text
. No puede, por supuesto, por lo que luego informa su error.
No puedo hablar de por qué funciona en un VS y no en el Otro, pero esto es lo que haría
rebautizar.
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
"Seleccionar" es una compilación en Nombre del método para otra lib común. así que recomiendo encarecidamente que cambie el nombre a algo así como "AppColumnSelectText" o lo que sea pero no "Seleccione".
luego cambia
public interface IColumn { }
A
public interface IColumn
{
string Text {get; set;}
}
luego implementarlo
public class MyClass : IColumn
{
public string Text { get; set;}
}
A continuación, transfiera su dinámica a IColumn, suponiendo que proceda del tipo de clase MyClass
var columns = fields.Select(a => new {a as IColumn}).ToList();
Entonces tu serás capaz de hacer
var result3 = columns.AppColumnSelectText(x => x.Text);
Sé que esto probablemente no es lo que estás buscando, pero la programación es más limpia y puedes archivar el mismo resultado.
ACTUALIZAR
Por favor, lea la siguiente y comentarios con suerte esto pinta una mejor imagen.
public static class Test
{
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
public static IColumn SelectOtherColumn<TResult>(this IColumn source, Func<IColumn, TResult> selector)
{
throw new NotImplementedException();
}
}
public interface IColumn
{
string Text { get; set; }
}
public class Program
{
private static void Main(string[] args)
{
IEnumerable ojb = new object[0];
IEnumerable<dynamic> dynX = ojb.Cast<dynamic>();
// CS1061 ''object'' does not contain a definition for ''Text''...
// var tooltip shows IColumn instead of IEnumerable<dynamic>
//NB this is the System.Linq.Select
var result = dynX.Select(x => x.Text);
var xzy = dynX as IColumn;
//converstion here will probably FAIL so this makes this pointless.
//here the compliter complains as the Type object has no Text Prop as it was not sepcified anywhere
var theThingyouwant1 = xzy.Select(x => x.Text);
//here you are OK as the complier can infer something
var theThingyouwant2 = xzy.SelectOtherColumn(x => x.Text);
}
}
Actualización adicional a esto ... Vea a continuación la ilustración
public class MyType
{
public string Text { get; set; }
public string SomethingEsle { get; set; }
}
public class Program
{
private static void Main(string[] args)
{
List<MyType> ojb = new List<MyType>();
ojb.Add(new MyType {Text = "OMG", SomethingEsle = "cat"});
//dynX is a dynamic...
var dynX = ojb.Select(x => new {x.Text, x.SomethingEsle}).ToList();
//NB this is the System.Linq.Select
//this now works as the complier can determine that there is a property Text
var result = dynX.Select(x => x.Text).ToList();
}
}
Podría usar su Seleccionar ... pero solo funciona en algo que implemente IColumn, por lo que me resulta difícil ver cómo funcionaría alguna vez.
Puedes usar esto
static void Main(string[] args)
{
//IEnumerable x = new object[0];
//var result2 = x.Select(_ => _.Text);
//Compile time Error "Enumerable" does not contain a definition for ''Select'' and no extension method
// because IEnumerable is not a generic class
IEnumerable<object> x = new object[0];
var result2 = x.Select(_ => _.Text);
}