MSDN.WhiteKnight - Stack Overflow answers
Ответ на ".NET Specification"
Answer 1131533
Есть ли языки, которые не могут без него жить?
Языкам, по существу, до лампочки этот аттрибут. Языкам требуется API, который они переваривают, а атрибут CLSCompliant они могут использовать, чтобы вывести предупреждение во время компиляции при попытке использовать неподдерживаемый API. А могут и не использовать. Реальность находится ближе ко второму варианту. Чтобы проиллюстировать это, создадим экспериментальную заготовку для взаимодействия "низкоуровневого" С# и высокоуровневого JScript.NET:
using System; using System.Collections.Generic; using Microsoft.JScript; using System.CodeDom.Compiler; using System.Text; namespace ConsoleApp1 { class Program { static object JsExecute( string script, string[] refs, string func, params object[] args) { var jsc = new JScriptCodeProvider(); var parameters = new CompilerParameters( refs, "test.dll", true); parameters.GenerateExecutable = false; CompilerResults results = jsc.CompileAssemblyFromFile(parameters, new string[] { script }); if (results.Errors.Count > 0) { Console.WriteLine("Errors: "); foreach (var err in results.Errors) { Console.WriteLine(err.ToString()); } return null; } var ass = results.CompiledAssembly; var c = ass.GetType("C"); var f = c.GetMethod(func); return f.Invoke(null, args); } } }
Чтобы это заработало, нужно добавить ссылку на Microsoft.JScript (и проект должен быть .NET Framework - в .NET Core нет нужных типов для интеграции с JS). Собственно, код берет скрипт из файла, компилирует и запускает отражением. Обращаю внимание, что
results.Errors
помимо ошибок включает также предупреждения, так что мы увидим все, что выдает компилятор (jsc). Теперь с его помощью мы можем прогнать интересные API через JS и посмотреть, как он себя поведет.Эксперимент 1 - UInt32
Начнем с простого - попробуем скормить JS "некомплиантый" UInt32:
JS:
class C { static function calc_sum(x, y) { return x + y; } }
C#:
static void Main(string[] args) { object res = JsExecute( "test.js", new[] { "mscorlib.dll", "System.Core.dll" }, "calc_sum", new object[] { (uint)1, (uint)2 }); Console.WriteLine(res); Console.Read(); } /* Вывод: 3 */
Как видно, все отработало без ошибок. На самом деле, это ничего толком не доказывает, так как JScript.NET, в отличие от браузерного эквивалента, поддерживает uint нативно (https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ddacxdt5%28v%3dvs.100%29). Но по крайней мере наш экспериментальный код работает.
Эксперимент 2 - Microsoft.Extensions.Configuration
Рассмотрим упомянутую Microsoft.Extensions.Configuration. К счастью, она совместима с новыми версиями .NET Framework. Для проверки напишем такой код на JS, который создает ConfigurationBuilder, устанавливает значение в словаре Properties и пытается считать его обратно:
import Microsoft.Extensions.Configuration; class C { static function test() { var builder = new ConfigurationBuilder(); builder.Properties['key'] = 'Value'; return builder.Properties['key']; } }
C#:
static void Main(string[] args) { object res = JsExecute( "test.js", new[] { "mscorlib.dll", "System.Core.dll", "Microsoft.Extensions.Configuration.dll" }, "test", new object[] { }); Console.WriteLine(res); Console.Read(); } /* Вывод: Value */
Опять же, все работает прекрасно. Ну, тоже нет ничего удивительно - все типы ложаться на систему типов JS.
Эксперимент 3 - Указатели
Теперь возьмем то, что JS действительно не поддерживает - указатели. Само собой, напрямую указатель мы даже передать не можем, но есть API типа Pointer.Box/Unbox, IntPtr которые позволяют заворачивать указатели для отражения. И они, как вы можете догадаться, CLSCompliant(false). Напишем код, который передает указатель в JS, а в коде на JS попытаемся поработать с ним доступными в нем средствами:
JS:
import System; import System.Reflection; import System.Runtime.InteropServices; class C { static function write_value(p) { var ptr = new IntPtr(Pointer.Unbox(p)); Marshal.WriteInt32(ptr, 1); } }
C#
unsafe static void Main(string[] args) { int x = 0; Console.WriteLine("x before: "+x.ToString()); object res = JsExecute( "test.js", new[] { "mscorlib.dll", "System.Core.dll", "System.Runtime.dll" }, "write_value", new object[] { Pointer.Box(&x, typeof(int*))}); Console.WriteLine("x after: " + x.ToString()); Console.Read(); } /* Вывод: x before: 0 x after: 1 */
Как видим, не только нет никаких предупреждений на использование 2 методов с CLSCompliant(false), но и код успешно смог установить значение переменной по указателю. Разгадка в том, что под капотом все работает на отражении, и JS нигде не встречает необходимость создать непосредственно переменную-указатель. Это не значит, что любые API на указателях можно использовать таким образом. Если API принимает не void*, а допустим byte* - уже непонятно, как его преобразовать.
Словом, значение CLSCompliantAttribute в современном мире .NET не так уж велико. Он нужен больше чтобы заткнуть сам компилятор C# и отдельные анализаторы FxCop, которые ругаются на его отсутствие, чем для реальной интеграции языков. Некоторые решают игнорировать предупреждение и не ставить атрибут, чтобы сэкономить на размере бинарника. Но если просто удалить атрибут, сломается код, который от него зависит, что и приводит к ситуации "не нужен даже создателям, но не удалить".
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.