Здравствуйте, VladD2, Вы писали:
VD>В общем, заэмитить это дело похоже можно. А вот считать через SRE, похоже нельзя. Остается или дублировать это дело с помощью процедур, или плюнуть на него.
У SR проблемы только с считыванием модификаторов у типов-аргументов. А что мешает ставить модификаторы на самом верхнем уровне? Ведь единственная их задача — обойти правило одинаковых сигнатур. Весь overload resolution всё равно будет на основе констрейнтов, которые записываются отдельно.
Здравствуйте, nikov, Вы писали:
N>У SR проблемы только с считыванием модификаторов у типов-аргументов. А что мешает ставить модификаторы на самом верхнем уровне? Ведь единственная их задача — обойти правило одинаковых сигнатур. Весь overload resolution всё равно будет на основе констрейнтов, которые записываются отдельно.
Если только для типов параметров, то проблем быть не должно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>В общем, заэмитить это дело похоже можно.
А надо ли разрешать перегрузку по констрейнтам, если тип-параметр метода не встречается в сигнатуре метода? И где ставить модификатор, если метод без параметров (есть только типы-параметры)?
Может быть, вообще всегда ставить модификатор на возвращаемое значение, ведь оно тоже считается частью сигнатуры в CIL. Надо проверить, можно ли прилепить модификатор на void.
Здравствуйте, nikov, Вы писали:
N>А надо ли разрешать перегрузку по констрейнтам, если тип-параметр метода не встречается в сигнатуре метода? И где ставить модификатор, если метод без параметров (есть только типы-параметры)?
Не знаю. На первый взгляд — нет.
Вот реалистичный пример:
using System;
using System.Console;
using Nemerle.Utility;
public module Program
{
Main() : void
{
mutable str1 : string;
mutable str2 : string = "test";
mutable val1 : int? = 1;
mutable val2 : int? = null;
mutable val3 : int = 1;
WriteLine(str1.IsNull());
WriteLine(str2.IsNull());
WriteLine(val1.IsNull());
WriteLine(val2.IsNull());
WriteLine(val3.IsNull()); // На сегодня этот не работает
_ = ReadLine();
}
public IsNull[T](this x : T?) : bool
where T: struct
{
!x.HasValue
}
// эта перегрузка конфликтует с IsNull[T](this x : T) : bool where T: classpublic IsNull[T](this x : T) : bool
where T: struct
{
false
}
public IsNull[T](this x : T) : bool
where T: class
{
x == null
}
}
Хотелось бы, чтобы это работало. Ведь вполне логично, что если констрэйны позволяют отличить одну перегрузку от другой, то проблем быть не должно.
Это даст возможность создавать специализированные функции которые будут более эффективны и менее ограничены в свободе действий.
Ну, а методы которые не содержат параметры типов в описании параметров метода являются весьма редким являением. Обычно — это некие средства преобразования типов вроде Cast() из Linq-а.
Даже не знаю нужно ли в них создавать разные реализации на базе констрэйнов.
N>Может быть, вообще всегда ставить модификатор на возвращаемое значение, ведь оно тоже считается частью сигнатуры в CIL. Надо проверить, можно ли прилепить модификатор на void.
А что есть какие-то проблемы с ассоциацией modreq с самим методом?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Это даст возможность создавать специализированные функции которые будут более эффективны и менее ограничены в свободе действий.
Частичная специализация?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, VladD2, Вы писали:
VD>Дык вроде бы в ссылке что я привел как раз было сказано, что считать эту байду с параметра нельзя. Или нельзя с самого параметро, но можно с его типа?
Нет, там написано, что если тип параметра — это дженерик или массив или указатель, то нельзя считать модификаторы с типов-аргументов этого дженерика или с типа элемента массива или указателя, а можно только с верхнего уровня.
Вот тебе прототип:
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq;
class Program
{
static void Main()
{
var assemblyName = new AssemblyName("modopt");
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.RunAndSave);
var fileName = assemblyName.Name + ".dll";
var moduleBuilder = assemblyBuilder.DefineDynamicModule(
assemblyName.Name,
fileName);
var modopt = moduleBuilder.DefineType(
"OverloadedOnConstraints",
TypeAttributes.Public).CreateType();
var typeBuilder = moduleBuilder.DefineType(
"MyType",
TypeAttributes.Public);
var foo1 = typeBuilder.DefineMethod("Foo", MethodAttributes.Public | MethodAttributes.Static);
foo1.SetSignature(typeof(void), null, null, foo1.DefineGenericParameters("T"), null, null);
foo1.DefineParameter(1, ParameterAttributes.None, "x");
foo1.GetILGenerator().Emit(OpCodes.Ret);
var foo2 = typeBuilder.DefineMethod("Foo", MethodAttributes.Public | MethodAttributes.Static);
var t = foo2.DefineGenericParameters("T")[0];
t.SetGenericParameterAttributes(GenericParameterAttributes.ReferenceTypeConstraint);
foo2.SetSignature(typeof(void), null, new[] { modopt }, new[] { t }, null, null);
foo2.DefineParameter(1, ParameterAttributes.None, "x");
foo2.GetILGenerator().Emit(OpCodes.Ret);
var myType = typeBuilder.CreateType();
var methods = myType.GetMethods(BindingFlags.Public|BindingFlags.Static);
foreach (var method in methods)
{
Console.WriteLine(method.ReturnParameter.GetOptionalCustomModifiers().FirstOrDefault());
}
assemblyBuilder.Save(fileName);
}
}
Рефлектор:
public class MyType
{
// Methodspublic static void Foo<T>(T x)
{
}
public static void modopt(OverloadedOnConstraints) Foo<T>(T x) where T: class
{
}
}
VD>Гы. Я тупо удалил из твоего прототипа "new[] { modopt }" замени его на null и код все равно сгенерировал два метода, один с констрэйном, другой — нет.
VD>Так что modopt не нужен.
Только твоя сборка не верифицируемая
Microsoft (R) .NET Framework PE Verifier. Version 4.0.21006.1
Copyright (c) Microsoft Corporation. All rights reserved.
[MD]: Error: Method has a duplicate, token=0x06000003. [token:0x06000002]
[MD]: Error: Method has a duplicate, token=0x06000002. [token:0x06000003]
2 Error(s) Verifying modopt.dll
И, я подозреваю, если сделать методы виртуальными, ты не сможешь написать для них override.
Здравствуйте, nikov, Вы писали:
N>Только твоя сборка не верифицируемая
N>
N>Microsoft (R) .NET Framework PE Verifier. Version 4.0.21006.1
N>Copyright (c) Microsoft Corporation. All rights reserved.
N>[MD]: Error: Method has a duplicate, token=0x06000003. [token:0x06000002]
N>[MD]: Error: Method has a duplicate, token=0x06000002. [token:0x06000003]
N>2 Error(s) Verifying modopt.dll
Да, это я проверить забыл.
Ну, стало быть без modopt-ов никуда.
Ладно, будет время попробую прикрутить это дело к Nemerle.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, nikov, Вы писали:
VD>>А как? Шарп тупо не делает между ними разницы.
N>Почему же? Он выбирает сигнатуру с наименьшим количеством modopt. Так что достаточно не лепить его на наиболее общий overload.
А, точно! Это я просто со своей сборкой где не было modopt эксперементировал.
Тогда остается только понять как выбирать метод к которому не лепить modopt. Можно конечно заставлять программиста это явно (например, атрибутом) указывать. Но как-то не красиво это.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
N>>Почему же? Он выбирает сигнатуру с наименьшим количеством modopt. Так что достаточно не лепить его на наиболее общий overload.
VD>А, точно! Это я просто со своей сборкой где не было modopt эксперементировал. VD>Тогда остается только понять как выбирать метод к которому не лепить modopt. Можно конечно заставлять программиста это явно (например, атрибутом) указывать. Но как-то не красиво это.
Я бы предложил по умолчанию выбирать метод с самыми слабыми констрейнтами (или вообще без констрейнтов), если такой имеется. Если нет самых слабых (например, у одного class, а у другого struct, или констрейнты на разных типах-параметрах) — то по умолчанию помечать modopt-ами все методы, из C# нельзя будет вызвать ни один из них.
Можно еще предусмотреть атрибут (например DefaultOverload), который позволил бы программисту переопределить поведение по умолчанию.