c# - 仅允许在 C# 中父类(super class)的程序集上实例化子类

标签 c# inheritance subclass .net-assembly instantiation

想象一下 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 的构造函数的人确实是这两种情况的 ClassImplAClassImplB 的默认构造函数)。

关于如何执行此操作的任何其他想法?或者我在这里遗漏了什么?

更新

想法是让 PCL 程序集从离线同步中抽象出主项目(和一些其他项目)。

鉴于此,我的 PCL 使用自己的数据库进行缓存,我想要的是只为数据库的每个记录提供一个实例(这样当属性发生变化时,所有分配的变量都将具有该值,我可以确保因为主项目中没有人能够创建这些类,它们将由处理单个实例的管理类提供给变量。

因为我正在使用 SQLite-net为此,由于它要求每个实例都有一个空构造函数,我需要一种方法来仅允许 SQLite 和 PCL 程序集创建那些在主项目程序集上声明的子类(即)

更新 2

如果可以通过Reflection 绕过解决方案,我没有问题,因为我的主要重点是防止人们做 new ClassImplA在主要项目上犯了一个简单的错误。但是,如果可能的话,我想拥有这样的东西 JsonConvert.DeserializeObject<ClassImplA>实际上会失败并出现异常。

最佳答案

我可能错了,但 access modifiers 都不是将允许您表达这样的约束 - 它们限制其他实体可以看到的内容,但是一旦他们看到它,他们就可以使用它。

  1. 你可以尝试使用StackTrace基类的构造函数中的类来检查谁在调用它:

    public class Base
    {
        public Base()
        {
            Console.WriteLine(
                new StackTrace()
                    .GetFrame(1)
                    .GetMethod()
                    .DeclaringType
                    .Assembly
                    .FullName);
        }
    }
    
    public class Derived : Base
    {
        public Derived() {        }
    }
    

通过处理一些特殊情况,它可能会与 Activator 类一起使用,但由于显而易见的原因(反射、容易出错的字符串/程序集处理),它不是最佳解决方案。

  1. 或者你可以使用一些 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 的实现,但只有您的程序集才有用。

  1. 不要试图阻止实体创建 - 使其无法使用以不当方式创建的实体

假设您可以控制所有生成和使用此类实体的类,您可以确保只能使用正确创建的实体。

它可以是一个原始的实体跟踪机制,甚至是一些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();
        ...;
    }
}

这无助于防止所有错误,但任何坚持“非法”实体的尝试都会被打脸,清楚地表明一个人做错了什么。

  1. 只需将其记录作为系统特殊性并保持原样。

一个人不能总是创建一个 non-leaky abstraction (实际上一个人基本上永远做不到)。在这种情况下,解决这个问题似乎要么很重要,要么对性能不利,或者同时兼而有之。

因此,与其深思熟虑这些问题,不如记录下所有实体都应通过特殊类创建。直接实例化的对象不能保证与系统的其余部分一起正常工作。

它可能看起来很糟糕,但以 Entity Framework 及其 gotchas 为例在延迟加载、代理对象、分离实体等方面。那就是一个众所周知的成熟库。

我并不是说你不应该尝试更好的东西,但这仍然是你可以随时求助的一个选择。

关于c# - 仅允许在 C# 中父类(super class)的程序集上实例化子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41779923/

相关文章:

C#内存不足异常-警告策略

scala - 多个备选方案之间的隐式搜索决策

java - 该程序无法使用子类而不是父类(super class)来运行

java - 子类化 ParseUser 时出现 ClassCastException (Parse.com)

c# - 无法解析 JSON 架构并解析引用

c# - 从 Web API 操作使用 HttpClient 调用外部 HTTP 服务

c++ - 不可访问的基类公共(public)成员变量

java - 抽象类中的同步

java - 将父类(super class)的对象转换为子类的对象会创建一个新对象吗?

c# - 在 try/finally 外部或内部初始化一次性资源