Вывод типов
От: Ziaw Россия  
Дата: 26.03.10 10:10
Оценка:
Пишу fluent интерфейс для конфигурации.

Проблема в выводе типов метода Action2, хотя типы в Action, Action3 и SomeCfgAction2 выводятся прекрасно.
    public class Cfg<T>
    {
        public void SomeCfgAction(Func<T> arg, string s)
        {
        }
        public void SomeCfgAction2<T2>(Func<T, T2> arg, string s)
        {
        }
    }

    public static class FluentCfgExts
    {
        public static TCfg Action<TCfg, T>(this TCfg cfg, Func<T> arg, string s)
            where TCfg: Cfg<T>
        {
            return cfg;
        }

        public static TCfg Action2<TCfg, T, T2>(this TCfg cfg, Func<T, T2> arg, string s)
            where TCfg: Cfg<T>
        {
            return cfg;
        }

        public static Cfg<T> Action3<T, T2>(this Cfg<T> cfg, Func<T, T2> arg, string s)
        {
            return cfg;
        }

        static void Main(string[] args)
        {
            var cfg = new Cfg<int>();
            cfg.Action(() => 1, ""); // ok
            cfg.Action2(i => 1.0, ""); // The type arguments for method cannot be inferred from the usage
            cfg.Action3(i => 1.0, ""); // ok
            cfg.SomeCfgAction2(i => 1.0, ""); // ok
        }
    }

    public class CfgExt<T> : Cfg<T>
    {
        public void SomeExtCfgAction(T arg)
        {
        }
    }


Как написать метод Action2 чтобы он работал для Cfg и CfgExt и возвращал соответственно их же?
Re: Вывод типов
От: Ziaw Россия  
Дата: 26.03.10 10:15
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Как написать метод Action2 чтобы он работал для Cfg и CfgExt и возвращал соответственно их же?


забыл указать, C# 3.0
в реальном приложении Cfg не используется, используются несколько его наследников, но основная масса методов находится в Cfg.
Re: Вывод типов
От: Пельмешко Россия blog
Дата: 26.03.10 10:47
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Пишу fluent интерфейс для конфигурации.


Z>Проблема в выводе типов метода Action2, хотя типы в Action, Action3 и SomeCfgAction2 выводятся прекрасно.

Z>
Z>            cfg.Action2(i => 1.0, ""); // The type arguments for method cannot be inferred from the usage
Z>


Z>Как написать метод Action2 чтобы он работал для Cfg и CfgExt и возвращал соответственно их же?


А как ВООБЩЕ только по лямбде i => 1.0 можно вывести тип T при приведении к Func<T, T2> arg, не задумывались?
Можно сделать с клиентской стороны так:
cfg.Action2((int i) => 1.0, "");
..то есть частично указать типы-параметры.

Других вариантов нет, в C# не настолько мощный вывод типов чтобы по неиспользованному имени в лямбде угадать какого оно типа
В других вариантах у Вас T выводится по другим аргументам, по Cfg<T>, например.
Re[2]: Вывод типов
От: Пельмешко Россия blog
Дата: 26.03.10 11:06
Оценка: 10 (1)
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, Ziaw, Вы писали:


Z>>Как написать метод Action2 чтобы он работал для Cfg и CfgExt и возвращал соответственно их же?


Z>забыл указать, C# 3.0


Изменения вывода типов в C# 4.0 влияют только на случаи приведения method group к типам делегатов, с лямбдами и анонимными методами ничего не изменилось.

Ваш сценарий вывода невозможен, так как вывод типов generic-методов в C# не умеет использовать constraint'ы для вывода типов других параметров:
public static TCfg Action4<TCfg, T>(this TCfg cfg)
    where TCfg : Cfg<T>
{
    return cfg;
}

cfg.Action4(); // The type arguments for method cannot be inferred from the usage
T может быть выведен только по другому аргументу, никак иначе.

p.s. Constraint'ы даже в сигнатуру то не входят, не используются в overload resolution.
Re[2]: Вывод типов
От: Ziaw Россия  
Дата: 26.03.10 11:17
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Других вариантов нет, в C# не настолько мощный вывод типов чтобы по неиспользованному имени в лямбде угадать какого оно типа

П>В других вариантах у Вас T выводится по другим аргументам, по Cfg<T>, например.

Жаль, хотя вполне ожидаем вывод из TCfg: Cfg<T>, первый аргумент нам точно известен.

Есть ли воркэраунд? Очень хочется fluent интерфес для потомков Cfg. Является ли написание идентичных Action2 для каждого потомка единственными выходом?
Re[3]: Вывод типов
От: Пельмешко Россия blog
Дата: 26.03.10 11:37
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, Пельмешко, Вы писали:


П>>Других вариантов нет, в C# не настолько мощный вывод типов чтобы по неиспользованному имени в лямбде угадать какого оно типа

П>>В других вариантах у Вас T выводится по другим аргументам, по Cfg<T>, например.

Z>Жаль, хотя вполне ожидаем вывод из TCfg: Cfg<T>, первый аргумент нам точно известен.


Кстати, F# выводит:
type Cfg<'T>() =
     member self.SomeCfgAction  (arg : unit -> 'T, s : string) = ()
     member self.SomeCfgAction2 (arg : 'T   -> 'U, s : string) = ()

type CfgExt<'T>() =
     inherit Cfg<'T>()
     member self.SomeExtAction (arg : 'T) = ()

let action  (cfg : #Cfg<'T>) (arg : unit -> 'T) s = cfg
let action2 (cfg : #Cfg<'T>) (arg : 'T   -> 'U) s = cfg
let action3 (cfg :  Cfg<'T>) (arg : 'T   -> 'U) s = cfg

let cfg = Cfg()
action  cfg (fun () -> 1)  "" |> ignore
action2 cfg (fun i -> 1.0) "" |> ignore
action3 cfg (fun i -> 1.0) "" |> ignore
cfg.SomeCfgAction2((fun i -> 1.0), "")

Но зато не получилось сделать такой extension, чтобы первый параметр был generic-типа (TCfg), возможно вообще нельзя...
Re: Вывод типов
От: Ziaw Россия  
Дата: 26.03.10 12:38
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Пишу fluent интерфейс для конфигурации.


Удалось решить частный случай, когда Cfg абстрактный класс с одним поколением наследников. Поскольку меня это устраивает я успокаиваюсь.

решение получилось такое:
    public abstract class Cfg<T, TThis>
        where TThis : Cfg<T, TThis>
    {
        public TThis Action2<T2>(Func<T, T2> arg, string s)
        {
            return (TThis)this;
        }
    }

    public class CfgExt<T> : Cfg<T, CfgExt<T>>
    {
        public CfgExt<T> ExtAction<T2>(Func<T, T2> arg, string s)
        {
            return this;
        }
    }

    public class CfgExt2<T> : Cfg<T, CfgExt2<T>>
    {
        public CfgExt2<T> Ext2Action<T2>(Func<T, T2> arg, string s)
        {
            return this;
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            CfgExt<int> test1 = new CfgExt<int>()
                .Action2(i => 1.0, "")
                .ExtAction(i => 1.0, "")
                .Action2(i => 1.0, "");

            CfgExt2<int> test2 = new CfgExt2<int>()
                .Action2(i => 1.0, "")
                .Ext2Action(i => 1.0, "")
                .Action2(i => 1.0, "");
        }
    }
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.