Конечная проблема с использованием this
вместо prototype
заключается в том, что при переопределении метода конструктор базового класса все равно будет ссылаться на переопределенный метод. Рассмотрим это:
BaseClass = function() {
var text = null;
this.setText = function(value) {
text = value + " BaseClass!";
};
this.getText = function() {
return text;
};
this.setText("Hello"); // This always calls BaseClass.setText()
};
SubClass = function() {
// setText is not overridden yet,
// so the constructor calls the superclass' method
BaseClass.call(this);
// Keeping a reference to the superclass' method
var super_setText = this.setText;
// Overriding
this.setText = function(value) {
super_setText.call(this, "SubClass says: " + value);
};
};
SubClass.prototype = new BaseClass();
var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!
subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!
по сравнению с:
BaseClass = function() {
this.setText("Hello"); // This calls the overridden method
};
BaseClass.prototype.setText = function(value) {
this.text = value + " BaseClass!";
};
BaseClass.prototype.getText = function() {
return this.text;
};
SubClass = function() {
// setText is already overridden, so this works as expected
BaseClass.call(this);
};
SubClass.prototype = new BaseClass();
SubClass.prototype.setText = function(value) {
BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};
var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!
Если вы считаете, что это не проблема, это зависит от того, можете ли вы жить без частных переменных и достаточно опытный, чтобы узнать утечку, когда вы ее видите. Кроме того, необходимость поставить логику конструктора после определения методов неудобна.
var A = function (param1) {
var privateVar = null; // Private variable
// Calling this.setPrivateVar(param1) here would be an error
this.setPrivateVar = function (value) {
privateVar = value;
console.log("setPrivateVar value set to: " + value);
// param1 is still here, possible memory leak
console.log("setPrivateVar has param1: " + param1);
};
// The constructor logic starts here possibly after
// many lines of code that define methods
this.setPrivateVar(param1); // This is valid
};
var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0
a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0
по сравнению с:
var A = function (param1) {
this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
this.publicVar = value; // No private variable
};
var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1
Альтернатива тому, чтобы быть ограниченным возвратом списка объектов должна была бы или гарантировать, чтобы A и B произошли из общей базы, вводят или реализуют единый интерфейс, затем возвращают список того базового типа или интерфейса. Включайте ограничение на Общий метод для того effect:-
List<ICommon> GetData<T>() where T: ICommon
{
}
Если нет определенная причина, что Вы не можете определить фактический тип заранее, можно просто сделать сам метод универсальным:
public void Main() {
List<A> a = GetData<A>();
}
public List<TType> GetData<TType>() {
List<TType> list= new List<TType>();
...
return list;
}
Вы не можете непосредственно возвратиться List<T>
как это.
, Почему? В основном, потому что List<A>
и List<B>
(или List<string>
по сравнению с List<int>
, который является тем же самым) рассматриваются как 2 полностью отдельных несвязанных класса.
Так же, как Вы не можете возвратиться string
из функции, которая, как объявляют, возвращается int
, Вы не можете возвратить Список строк от функции, которая, как объявляют, возвращает список ints. <T>
вот определенный отвлекающий маневр. Вы не могли записать общий метод, который возвратил обе строки и ints также...
См. здесь для большего количества информации о такой вещи.
Поэтому то, что необходимо сделать, возвратить что-то, что оба типа получают из (что они "имеют общего".)
Как [1 112] John Rasch говорит , Вы могли возвратиться IList
, (отметьте НЕ дженерик, таким образом, это - просто список object
с, или просто возвратите его как object
. К сожалению, нет никакого способа сохранить тип списка.
РЕДАКТИРОВАНИЕ на ответ Orion ниже, добавленное ограничение, которое AnthonyWJones предложил
Вы, вероятно, должно иметь интерфейс/абстрактный класс, который A и B наследовали от
public interface IMyInterface { }
public class A : IMyInterface { }
public class B : IMyInterface { }
public List<IMyInterface> GetData<T>() where T : IMyInterface
{
List<IMyInterface> myList = new List<IMyInterface>();
if (typeof(T) == typeof(A))
{
myList.Add(new A());
}
if (typeof(T) == typeof(B))
{
myList.Add(new B());
}
return myList;
}
Если Вы не знаете тип, Вы хотите до времени выполнения, то дженерики являются, вероятно, неправильным инструментом для задания.
, Если Ваша функция значительно изменяет поведение (как изменяющийся тип возврата) на основе аргумента, то это должны, вероятно, быть две функции.
похоже, что эта функция не должна быть родовой, и должна на самом деле быть двумя функциями.
public void Main() {
List<A> a = GetDataA();
}
public List<A> GetDataA() {
List<A> a= new List<A>()
...
return a;
}
public List<B> GetDataB() {
List<B> b= new List<B>()
...
return b;
}
Недавно мне пришлось решать похожую проблему, где ни одно из предложенных решений не было удовлетворительным; ограничение параметра типа было непрактичным. Вместо этого я позволяю потребителям метода решать, как изменять данные . Например, вы можете написать общую версию String.Split (), которая возвращает строго типизированный список, при условии, что вы укажете ему, как преобразовать подстроки в T.
Как только вы захотите переложить ответственность вверх по стеку вызовов ( и вам будет удобно передавать лямбды), вы можете обобщить этот шаблон произвольно. Например, если способ GetData () варьируется (как, по-видимому, предполагают некоторые ответы), вы также можете поднять эту функцию в область действия вызывающего.
Демо:
static void Main(string[] args)
{
var parseMe = "Hello world! 1, 2, 3, DEADBEEF";
// Don't need to write a fully generic Process() method just to parse strings -- you could
// combine the Split & Convert into one method and eliminate 2/3 of the type parameters
List<string> sentences = parseMe.Split('!', str => str);
List<int> numbers = sentences[1].Split(',', str => Int32.Parse(str, NumberStyles.AllowHexSpecifier | NumberStyles.AllowLeadingWhite));
// Something a little more interesting
var lettersPerSentence = Process(sentences,
sList => from s in sList select s.ToCharArray(),
chars => chars.Count(c => Char.IsLetter(c)));
}
static List<T> Split<T>(this string str, char separator, Func<string, T> Convert)
{
return Process(str, s => s.Split(separator), Convert).ToList();
}
static IEnumerable<TOutput> Process<TInput, TData, TOutput>(TInput input, Func<TInput, IEnumerable<TData>> GetData, Func<TData, TOutput> Convert)
{
return from datum in GetData(input)
select Convert(datum);
}
Гуру функционального программирования, вероятно, будут зевать на это исследование: " ты'