想象一下Xamarin解决方案中的以下场景:
装配A(PCL):
public abstract class MyBaseClass
{
public MyBaseClass()
{
[...]
}
[...]
}
大会B(第三方图书馆):
public class SomeLibClass
{
[...]
public void MethodThatCreatesClass(Type classType){
[...]
//I want to allow this to work
var obj = Activator.CreateInstance(classType);
[...]
}
[...]
}
程序集C(主项目):
public class ClassImplA:MyBaseClass{
[...]
}
public class ClassImplA:MyBaseClass{
[...]
}
public class TheProblem{
public void AnExample(){
[...]
//I want to block these instantiations for this Assembly and any other with subclasses of MyBaseClass
var obj1 = new ClassImplA()
var obj2 = new ClassImplB()
[...]
}
}
如何防止子类在自己的程序集上实例化,只允许在超类和第三方库上使用它们(使用Activator.CreateInstance
)?
尝试1
我虽然可以使用internal constructor
创建基类但是后来我看到了多么愚蠢,因为子类不能继承构造函数,所以它们不会能够继承超类。
尝试2
我尝试在基类上使用Assembly.GetCallingAssembly
,但这在PCL项目中不可用。我找到的解决方案是通过反射调用它,但它也没有工作,因为基类的结果对于两种情况都是Assembly C
(我认为这是因为谁调用MyBaseClass
的构造函数确实是ClassImplA
和ClassImplB
的默认构造函数。)
关于如何做到这一点的任何其他想法?或者我在这里遗漏了什么?
更新
我们的想法是让PCL程序集从离线同步中抽象出主项目(以及其他一些项目)。
鉴于此,我的PCL使用自己的数据库进行缓存,我想要的只是为每个DB记录提供一个实例(这样当属性发生变化时,所有分配的变量都将具有该值,我可以确保因为主项目中没有人能够创建这些类,并且它们将由管理器类提供给变量,它将处理单个实例。
由于我正在使用SQLite-net,因为它要求每个实例都有一个空构造函数,所以我需要一种方法只能允许SQLite和PCL程序集用于创建在主项目程序集(ies)上声明的子类
更新2
如果可以通过反射绕过这个解决方案,我没有问题,因为我主要关注的是防止人们在主项目上做出new ClassImplA
的简单错误。但是,如果可能的话,我希望这样做,以便像JsonConvert.DeserializeObject<ClassImplA>
这样的东西实际上会因异常而失败。
答案 0 :(得分:1)
我可能错了,但access modifiers都没有允许你表达这些限制 - 它们限制了其他实体可以看到的东西,但一旦他们看到它,他们就可以使用它。
您可以尝试在基类的构造函数中使用StackTrace
类来检查谁在调用它:
public class Base
{
public Base()
{
Console.WriteLine(
new StackTrace()
.GetFrame(1)
.GetMethod()
.DeclaringType
.Assembly
.FullName);
}
}
public class Derived : Base
{
public Derived() { }
}
通过一些特殊情况处理,它可能适用于Activator
类,但由于显而易见的原因(反射,容易出错的字符串/汇编处理)而不是最佳解决方案。
或者您可以使用一些 dependency 来执行任何实质内容,并且该依赖项只能由您的主程序集提供:
public interface ICritical
{
// Required to do any real job
IntPtr CriticalHandle { get; }
}
public class Base
{
public Base(ICritical critical)
{
if (!(critical is MyOnlyTrueImplementation))
throw ...
}
}
public class Derived : Base
{
// They can't have a constructor without ICritical and you can check that you are getting you own ICritical implementation.
public Derived(ICritical critical) : base(critical)
{ }
}
好吧,其他程序集可能会提供ICritical
的实现,但是你的实现是唯一可以做任何好事的程序。
假设您可以控制生成和使用此类实体的所有类,您可以确保只能使用正确创建的实体。
它可以是原始实体跟踪机制,甚至是一些dynamic proxy wrapping
public class Context : IDisposable
{
private HashSet<Object> _entities;
public TEntity Create<TEntity>()
{
var entity = ThirdPartyLib.Create(typeof(TEntity));
_entities.Add(entity);
return entity;
}
public void Save<TEntity>(TEntity entity)
{
if (!_entities.Contains(entity))
throw new InvalidOperationException();
...;
}
}
防止所有错误都无济于事,但任何企图坚持“非法”实体的行为都会在脸上爆炸,清楚地表明一个人做错了。
一个人不能总是创建一个non-leaky abstraction(实际上一个基本上永远不可能)。在这种情况下,似乎解决这个问题要么是重要的,要么是性能不好,或者两者兼而有之。
因此,我们可以只记录所有实体应该通过特殊类创建,而不是沉溺于这些问题。直接实例化的对象不能保证与系统的其余部分一起正常工作。
它可能看起来很糟糕,但是,例如,实体框架在Lazy-Loading中使用gotchas,代理对象,分离实体等等。这是一个着名的成熟图书馆。
我不认为你不应该尝试更好的东西,但这仍然是你总是可以选择的选择。