2016-07-28 2 views
0

Я пытаюсь использовать Generics для передачи в struct функции, а затем эту функцию получить доступ к ее свойствам. Я хочу использовать Generics больше, потому что они должны быть «безопасными и эффективными». Вот как выглядит мой код.Могу ли я использовать Generics для получения свойства переданной структуры?

Класс

class foo { 
    struct A { 
     int a; 
     int b 
    } 

    struct B { 
     int a; 
     int b; 
    } 
} 

Generic Function

void bar<T> (T input) { 
    var a = input.a;  //this is where its says it know nothing about a 
} 

Вызов функции

bar<foo.A> (Aobj); 

Can Дженерики использоваться как это? (То есть доступ к членам данных объекта ...) Или все, что я могу сделать, это создать перегруженный метод? Единственные части объектов, которые я пытаюсь получить от функции, являются общими для обоих типов, поэтому я также подумал, что могу использовать Generics.

+0

Вам нужно будет использовать refection для получения свойств –

+0

Почему вы хотите использовать дженерики здесь? –

+2

Вы можете использовать общий интерфейс. –

ответ

3

Использование дженериков не означает, что вы можете использовать утка печатать. C# является статически типизированным языком, так что вам нужно сделать что-то вроде следующего:

Во-первых, определить интерфейс, который определяет общий интерфейс A и B:

interface IHasAB 
{ 
    int A { get; } 
    int B { get; } 
} 

Тогда, ваши struct реализуем этот интерфейс:

struct A : IHasAB 
{ 
    public A(int a, int b) 
    { 
     A = a; 
     B = b; 
    } 

    public int A { get; } // Note: `A` and `B` are now properties, not fields 
    public int B { get; } // like before; interfaces do not allow you to declare fields. 

    // Note also: mutable structs are a bad idea! That's why we implement 
    // `A` and `B` as read-only properties. 
} 

struct B : IHasAB { … } // same as for `A` 

Теперь вы можете реализовать метод bar с дополнительным параметром ограничения общего типа where T : IHasAB:

void bar<T> (T input) where T : IHasAB 
{ 
    var a = input.A; 
} 

Ограничение на T позволяет компилятору знать, что он может ожидать T быть. Теперь он знает, что T - это тип, который реализует IHasAB и поэтому имеет два свойства A и B (так как это интерфейс состоит из). Вот почему теперь вы можете получить доступ к .A по адресу input.

Обратите внимание, что вам не обязательно нужно сделать bar родовое:

void bar(IHasAB input) 
{ 
    var a = input.A; 
} 

В этом последнем случае, обратите внимание, что input теперь ссылка типизированных параметров. Если вы передали один из своих структур A или B этому методу, он должен быть помещен в коробку (т. Е. Повернут из типа значения в ссылочный тип). В этом есть некоторые накладные расходы.

Я не буду углубляться в опасности изменчивых структур и бокса, которые могут легко произойти в ситуациях, когда структуры реализуют интерфейсы, поскольку это совсем другое дело; Я бы рекомендовал вам потратить немного времени на изучение этих тем. ;-)

+0

Обратите внимание, что 'A' и' B' являются структурами, а не свойствами. –

+0

@PatrickHofman: Вы имеете в виду типы 'A' и' B', или свойства 'A' ​​и' B'? В первом случае они были 'struct' before, и они все еще есть. (Да, кстати, мне, вероятно, не хватает намека на бокс.) В последнем случае вы, вероятно, имели в виду, что теперь они являются свойствами, а не полями. – stakx

+0

Да, извините. Вы просто показывали структуру, а не класс. –

-1

Использовать dynamic.

void bar<T> (T input) { 
    dynamic a = input.a;  //this is where its says it know nothing about a 
} 
+1

Нет, это не имеет никакого отношения к использованию дженериков. – Glubus

+0

Конечно. –

+1

Ваш код поражает цель общего. –

0

Вы должны бросить T к конкретному типу до обращения к члену как

void bar<T> (T input) 
{ 
    var obj = T as foo; 
    if (obj != null) 
     var a = obj.A;  
} 
+0

Почему бы не сузить 'T' используя' где T: foo'? Но эй, что такое использование родословной? –

+0

И кроме того, нет свойства или поля 'a' в' foo'. Ваш код тоже не работает. –

+0

@PatrickHofman, точно и посмотрите, что прокомментировал, но это за то, что OP размещено как вопрос. – Rahul

0

Вам нужно указать компилятору, какого типа вы ожидаете от T. Ваш метод bar<T> абсолютно ничего не знает о T, за исключением того, что он должен быть типом, который по крайней мере типа object или подкласс object. Это означает, что вы можете использовать все методы, которые определены в object по адресу T.

Однако, если вы хотите, чтобы иметь возможность использовать a, вы должны сказать, что bar<T>T будет иметь тип, который реализует свойство a. Вот где интерфейсы вступают в игру. Это означает, что вам нужно создать суперкласс или еще лучше интерфейс, который требует реализации a, поэтому вы можете сообщить bar<T>, что T является, по меньшей мере, типом <your interface>. См. Приведенный ниже код:

interface ImanInterfaceWithA { 
    int a {get; set;} 
} 

class foo { 
    struct A : ImanInterfaceWithA { 
     public int a {get; set;} 
     public int b {get; set;} 
    } 

    struct B : ImanInterfaceWithA { 
     public int a {get; set;} 
     public int b {get; set;} 
    } 


    void bar<T> (T input) where T : IamanInterfaceWithA 
    { 
     var a = input.a; 
    } 
} 
+0

Исправлено, за исключением того, что интерфейсам не разрешено иметь поля (переменные), только свойства. См. Ответ 'stakx'. –

+0

Вы абсолютно правы, конечно. Я очищаю свой ответ. – Glubus

Смежные вопросы