想象一下 Xamarin 解决方案中的以下场景:
程序集 A (PCL):
public abstract class MyBaseClass
{
public MyBaseClass()
{
[...]
}
[...]
}
程序集 B(第 3 方库):
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()
[...]
}
}
如何防止子类在它们自己的程序集中实例化,并只允许它们在父类(super class)和第 3 方库中实例化(使用 Activator.CreateInstance
)?
尝试 1
虽然我可以用 internal constructor
创建基类但是后来,我发现那是多么愚蠢,因为子类无法继承构造函数,因此它们无法从父类(super class)继承。
尝试 2
我尝试使用 Assembly.GetCallingAssembly
在基类上,但在 PCL 项目中不可用。我找到的解决方案是通过反射调用它,但它也不起作用,因为基类的结果将是 Assembly C
。对于这两种情况(我认为那是因为调用 MyBaseClass
的构造函数的人确实是这两种情况的 ClassImplA
和 ClassImplB
的默认构造函数)。
关于如何执行此操作的任何其他想法?或者我在这里遗漏了什么?
更新
想法是让 PCL 程序集从离线同步中抽象出主项目(和一些其他项目)。
鉴于此,我的 PCL 使用自己的数据库进行缓存,我想要的是只为数据库的每个记录提供一个实例(这样当属性发生变化时,所有分配的变量都将具有该值,我可以确保因为主项目中没有人能够创建这些类,它们将由处理单个实例的管理类提供给变量。
因为我正在使用 SQLite-net为此,由于它要求每个实例都有一个空构造函数,我需要一种方法来仅允许 SQLite 和 PCL 程序集创建那些在主项目程序集上声明的子类(即)
更新 2
如果可以通过Reflection 绕过解决方案,我没有问题,因为我的主要重点是防止人们做 new ClassImplA
在主要项目上犯了一个简单的错误。但是,如果可能的话,我想拥有这样的东西 JsonConvert.DeserializeObject<ClassImplA>
实际上会失败并出现异常。
最佳答案
我可能错了,但 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 (实际上一个人基本上永远做不到)。在这种情况下,解决这个问题似乎要么很重要,要么对性能不利,或者同时兼而有之。
因此,与其深思熟虑这些问题,不如记录下所有实体都应通过特殊类创建。直接实例化的对象不能保证与系统的其余部分一起正常工作。
它可能看起来很糟糕,但以 Entity Framework 及其 gotchas 为例在延迟加载、代理对象、分离实体等方面。那就是一个众所周知的成熟库。
我并不是说你不应该尝试更好的东西,但这仍然是你可以随时求助的一个选择。
关于c# - 仅允许在 C# 中父类(super class)的程序集上实例化子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41779923/