有没有办法通过"这个"到基础构造函数?
abstract class Base<TSelf>
{
public ICollection<TSelf> List;
public Base(ICollection<TSelf> list, TSelf self)
{
List = list;
List.Add(self);
}
}
class Implementation : Base<Implementation>
{
public Implementation() : base(new List<Implementation>(), this)
{
}
}
显然,Implementation
的构造函数中存在编译错误,其中this
传递给base
。
我也认为没有办法在list
级别实例化Base
。
答案 0 :(得分:7)
不,您不能在构造函数初始值设定项中使用this
。您之后必须添加调用Add(this)
- 但只要您转换为Base<TSelf>
,就可以在TSelf
构造函数中执行此操作。在转换为this
之前,您需要先将object
转换为TSelf
,但出于相当复杂的原因,不幸的是,类型参数允许转换。
您可以在List<TSelf>
构造函数中创建Base
,但没有问题。以下示例代码显示了这两个:
abstract class Base<TSelf>
{
// Let's make it a property rather than a public field...
public ICollection<TSelf> List { get; }
public Base()
{
List = new List<TSelf>();
// This will obviously fail if you try to create a `Base<Foo>`
// from a class that isn't a Foo
TSelf selfThis = (TSelf) (object) this;
List.Add(selfThis);
}
}
class Implementation : Base<Implementation>
{
}
您可以向TSelf
添加约束,以使投射失败不太可能,但并非不可能:
abstract class Base<TSelf> where TSelf : Base<TSelf>
这并不能阻止你写作
class Implementation : Base<Implementation> {}
class Evil : Base<Implementation> {}
然后,当您构建Evil
的实例时,您正尝试向Evil
添加List<Implementation>
引用,但这些引用无法正常工作...并且转换无法停止你走得那么远。
答案 1 :(得分:0)
正如@Jon Skeet所说,您不能通过常规的旧构造函数调用传递this
。但是,可以使用反射和FormatterServices
类来实现。下面的示例代码使用工厂模式以提高可读性。
基类实现:
public abstract class Model<TSelf>
where TSelf : Model<TSelf>
{
public ICollection<TSelf> Items { get; }
protected Model(TSelf self, ICollection<TSelf> items)
{
if ((self == null) || (items == null))
{
throw new ArgumentNullException();
}
if (self != this)
{
throw new ArgumentException();
}
Items = items;
Items.Add(self);
}
}
派生的类实现:
public sealed class ModelImplementation
: Model<ModelImplementation>
{
private ModelImplementation(ModelImplementation self)
: base(self, new List<ModelImplementation>()) { }
}
此技术的主要内容是ModelFactory
类,该类接受一个未初始化的对象并手动对其调用适当的构造函数。可以通过修改GetConstructor
和Invoke
调用来实现对其他构造函数参数的支持。
您应该致电ModelFactory.Create<ModelImplementation>()
以获得新的ModelImplementation
来代替new
关键字。
工厂类实现:
public static class ModelFactory
{
public static Model<TSelf> Create<TSelf>()
where TSelf : Model<TSelf>
{
var result = FormatterServices
.GetUninitializedObject(typeof(TSelf));
result.GetType()
.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[] { typeof(TSelf) },
null)
.Invoke(
result,
new[] { result });
return (TSelf)result;
}
}