2012-01-16 3 views
3

Если у меня есть следующий метод;Получить модель из общей модели

private TSource CopyFileClientModel<TSource>(TSource fileClientOrContact) 

Так в этот метод, я могу передать в любой модели FileClient или FileContact.

Оба имеют одно свойство Contact, которое также является моделью, а также некоторыми другими свойствами, не являющимися общими между ними.

Теперь я хочу получить эту модель Contact из пройденной модели.

Contact sourceContact = fileClientOrContact.Contact; 

Однако, учитывая, что это родовое, он не знает, что есть Contact объект в TSource.

Мое ограничение заключается в том, что я не могу разместить интерфейс против FileClient или FileContact. В принципе я не могу тронуть ни одну из этих моделей.

Как я могу получить контактный объект от TSource? Могу ли я каким-то образом использовать отражение?

+2

Почему «дженерики»? метод перегрузки, кажется, хорош здесь. – gdoron

+0

@gdoron - договорился – Skyrim

+0

@ gdoron, я думаю, может быть, я пытался переоценить это. если вы положите перегрузку в ответ, я с радостью помету его. – griegs

ответ

2

Вы можете передать функцию, чтобы вернуть контакт.

Например:

private FileClient CopyFileClientModel(FileClient fileClient) { 
    return this.CopyFileClientModel(fileClient, c => c.Client); 
} 

private FileContact CopyFileClientModel(FileContact fileContact) { 
    return this.CopyFileClientModel(fileContact, c => c.Client); 
} 

private TSource CopyFileClientModel<TSource>(TSource fileClientOrContact, Func<TSource, Contact> contactGetter) { 
    var contact = contactGetter(fileClientOrContact); 
    // Whatever else... 
} 
+0

Решил спуститься по этому маршруту. благодаря – griegs

2

Вы можете использовать Visitor pattern для обертывания двух классов и наследования двух классов посетителя из общего интерфейса.

По существу, вы должны создать новый класс с дублирующими методами/свойствами FileClient или FileContact и передать экземпляр класса в конструкторе. Все методы и свойства привязаны к фактическим в экземпляре класса, который вы храните. Оба ваших класса посетителя будут иметь свойство Contact, поэтому вы можете рассказать классам посетителей наследовать от общего интерфейса.

+0

Это, кажется, умное решение; почему там был нисходящий поток? Я хотел бы понять, есть ли антипаттерн или что-то связанное. – Skyrim

+0

Downvote был потому, что изначально я предложил интерфейс, не читая последний бит вопроса. К тому времени, как я отредактировал ответ, был пониженный уровень. –

+0

@ Скирим, хотя я не спустил вниз! Я думаю, что это слишком большая работа. И я думаю, что эти два класса должны иметь метод «Посещение», не так ли? Дизайн шаблона «адаптер» лучше. – gdoron

1

Что-то вроде?

if (fileClientorContact is FileContact) 
    return fileClientorContact as Contact; 
else 
    return ((Client)fileClientorContact).Contact; 

Конечно, это предполагает «контакт» в fileClientorContact того же типа вы хотите вернуться. Кроме того, вы можете использовать следующее, если оба типа определяет Contact свойства, как вы предлагаете в вашем вопросе:

var propInfo = fileClientorContact.GetType().GetProperty("Contact"); 
if (propInfo == null) 
    return false; // replace with something appropriate 

return propInfo.GetValue(fileClientorClient, null, null, null) as Contact; 

Это примерно из памяти, но идея должна быть в силе.

+0

@griegs - Я пропустил точку вашего вопроса? –

+0

Да, я немного думаю. Мне нужно получить значение свойства из общей модели, которая не может наследовать интерфейс, поэтому компилятор не будет знать, что свойство существует во время разработки. и используя 3.5 not 4.0 – griegs

+0

Оба примера должны сделать это (в разных контекстах). Является ли имя свойства, которое возвращает объект, который вы ищете, не названный «Контакт»? Вы знаете имя свойства, которое должно использоваться, или вам нужно определить его также во время выполнения? –

1

если вы используете C# 4.0 вы можете использовать динамику

dyanmic obj = fileClientOrContact; 
Contact sourceContact = obj.Contact; 

Это не идеально, хотя! (Это может вызвать исключение времени выполнения)


EDIT

С другой стороны, я бы, вероятно, пойти с двумя функциями перегрузки для каждого типа, с каждой функцией, вызывающей общую функцией для общей функциональности.

+0

Нет, это было бы очень приятно, но нет. – griegs

+0

@griegs - почему это не сработает? кстати, 'dyanamic' должен' dynamic'. Я только что проверил это на своем компиляторе. – Skyrim

+0

@griegs - ах ... извините, lol !! – Skyrim

2

Вы можете использовать динамический режим, если используете.NET 4:

private TSource CopyFileClientModel<TSource>(TSource fileClientOrContact) 
(
    dynamic d = fileClientOrContact; 

    // Now you reach the Contact property 
    var x = d.Contact; 
) 

Это будет разумно сделать проверку, что typeof(TSoruce) является FileClient или FileContact

Другая вещь, которую вы можете сделать, это обернуть эти два класса. см adapter design pattern в Википедии


Но, может быть, Generics не подходит для вас. Почему бы не использовать метод перегрузки с FileClient и FileContact. Ваша подпись метода принимает любой TSource, но когда метод get int, long, Person это недопустимо, значит, Generics здесь не является правильным решением, если вы можете ограничить TSource тем, кого вы ожидаете.

Вместо этого используйте Method overload. и сохранить эти «творческие решения».

+0

извините, используя 3.5 – griegs

+0

err, вы не хотите этого делать, b/c 'x' будет динамичным , и вы знаете, какой тип вы ожидаете от 'x'! – Skyrim

+0

@Skyrim, динамический не пуленепробиваемый. Это только одно из предложенных мной решений. – gdoron

0

Если FileContact и FileClient доли нет предков, которые имеют .Contact, и вы не в состоянии реализовать общий интерфейс для него, и вы действительно хотите использовать отражение, то вы могли бы сделать что-то например:

var sourceProp = typeof(TSource).GetProperty("Source"); 
    Contact contact; 
    if(sourceProp != null) 
    { 
     contact = (Contact)sourceProp.getValue(fileClientOrContact, null) 
    } 
Смежные вопросы