2014-09-18 2 views
0

Предоставлено класс и подклассМожет родовое коллекция предотвратить добавление производных объектов

public class Base 
{ 
    public string Name { get; set; } 
} 

public class Derived : Base 
{ 
    public string Extra {get; set; } 
} 

и общий список

var list = new List<Base>(); 

Я хочу, чтобы предотвратить это:

var b = new Base { Name = "base" }; 
var d = new Derived { Name = "derived", Extra = "stuff" }; 

list.Add(b); // this is good 
list.Add(d); // this is BAD 

Причиной для предотвращения этого является список, который будет сериализован таким образом, который теряет информацию о типе, затем ресериализуется до List<Base>. Любые элементы, получаемые frome Base, потребуют downcasting для типа, неизвестного десериализатором (я, конечно, не хочу использовать отражение, чтобы найти класс, который наследует от Base и имеет свойство «Extra»). Возможно, я буду бороться с подходами, чтобы решить это, и это может привести к другому вопросу. Но на данный момент я могу избежать проблемы, не добавляя добавленные производные объекты в общий список?

+0

Они _may_ будут полезны ... Как поместить разрозненные объекты в список и сериализовать/десериализовать их и сохранить всю информацию о типе: https://weblogs.asp.net/stevewellens/serializing-arraylists-of-disparate-objects -to-xml-strings –

+2

Даже если бы вы могли, вы не должны: http://en.wikipedia.org/wiki/Liskov_substitution_principle – Mephy

+0

@Mephy: если я понимаю, как здесь применяется Лисков, возможно, ответ заключается в том, чтобы опрокинуть любые производные объекты, которые были добавлены до сериализации. В этом примере это разделило бы «Экстра». Тогда десериализация будет видеть только единый список. Это имеет большой смысл. – biscuit314

ответ

1

Нет, это не возможно с помощью системы C# типа. Вся посылка наследования состоит в том, что производные объекты могут обрабатываться так, как если бы они являлись экземпляром любого из их базовых типов (или интерфейсов).

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

+0

Я принял этот ответ, потому что, хотя в моем вопросе не было ясно, я хотел знать, был ли декларативный способ выполнить это. Спасибо тем, кто предложил пользовательский список/проверил подход Add() (спасибо @ user2711965 за то, что он первый, и «decPL» за предоставление образца). Некоторая версия этого вполне может быть тем, что я буду делать, но поскольку «Servy» указал в другом комментарии, просто подклассификация списка не препятствует доступу. Инкапсуляция (как с decPL) фиксирует контейнер на один тип (ну, если я не обобщил контейнер тоже ... hmmmm). – biscuit314

1

Почему не простая проверка:

if(!obj is Derived) 
{ 
    list.Add(obj); 
} 

Если вы хотите сравнить с точно базового класса, то вы можете сделать:

if(obj != null && obj.GetType() == typeof(Base)) 
{ 
    list.Add(obj); 
} 

Помните, эти проверки не может предотвратить код без этих проверок, добавить объект дочернего класса в список. Другой вариант заключается в создании пользовательского класса List<T>, выводя из List<T> и поставить метод new Добавить как объяснено в this answer.

+1

Простое решение. Но что, если OP будет иметь много классов, полученных из базы, или такие классы будут созданы в будущем? –

+0

@ AndyKorneyev, это действительная точка. Я также обновляю свой ответ против точного сравнения базового класса. – user2711965

+1

Ни одно из этих решений не позволяет кому-то добавлять производный тип в «Список». Некоторый другой код все равно сможет добавить производный тип в список. – Servy

3
public class UberStrongTypedList<T> : IList<T> 
{ 
    private readonly IList<T> _list = new List<T>(); 

    public void Add(T item) 
    { 
     if (item.GetType() == typeof (T)) 
      _list.Add(item); 
     else 
      ;//some error 
    } 

    public void Clear() 
    { 
     _list.Clear(); 
    } 

    //SNIP... 
} 
-1

Подкласс список:

private class Parent 
{ 
    public int ID { get; set; } 
} 

private class Child : Parent 
{ 

} 

private class MyList : List<Parent> 
{ 
    public new void Add(Parent item) 
    { 
     //runtime type check in case a Parent-derived object reaches here 
     if (item.GetType() == typeof(Parent)) 
      base.Add(item); 
     else 
      throw new ArgumentException("List only supports Parent types"); 
    } 

    [Obsolete("You can't add Child types to this list", true)] 
    public void Add(Child item) 
    { 
     throw new NotImplementedException(); 
    } 
} 

static void Main(string[] args) 
{ 
    var list = new MyList(); 
    list.Add(new Parent() { ID = 1 }); 
    list.Add(new Child() { ID = 2 }); //This gives a compiler error 
} 

Обратите внимание, что Устаревший атрибут с истинным параметром является то, что вызывает ошибку компилятора.

+1

Теперь вы добавляете новый класс 'Child2: Parent' и' list.Add (новый Child2() {ID = 3}); 'будет компилироваться правильно. – Mephy

+1

Это позволяет использовать список list.Add ((Parent) new Child() {ID = 2}); 'или' ((Список )) .Add (новый Child() {ID = 2}); ' – Servy

+0

Это правда. Если OP имеет жесткий контроль над кодом и может гарантировать отсутствие «Child2», OP получит мое решение, поскольку оно дает ошибку времени компиляции. Я могу обновить свой ответ, чтобы включить проверку типа. – user2023861

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