C# 关于 构造函数问题 关于对象实例化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 关于 构造函数问题 关于对象实例化相关的知识,希望对你有一定的参考价值。

下面这个构造函数部分中 GamePage这个对象的引用 这样也可以吗? 不声明对象 也不实例化就能调用了?这个就是所谓的构造函数在对象实例化时自动调用吗?? 不懂啊 。。。 我刚学不久 求 大哥们细心解答 谢谢

你把鼠标指向GamPage,看看这个对象的类型是什么

然后按F12,你就可以看到它的声明

动态类型肯定要实例化才能调用

我觉得它是一个你设计器拖的控件,或者是控件的成员;我认为它已经在设计器里面定义了

你按F12不出意外会到MenuForm.designer.cs里面,它就是设计器的代码;是你拖动控件时visual studio自动生成的,MenuForm的构造函数的InitializeComponent方法就定义了所有的设计器对象

构造函数是对象实例化时首个被调用的方法,你可以在你的项目上右键→调试→进入并单步调试新实例;然后按F11逐语句查看代码执行过程

参考技术A GamePage是类MenuFrom的一个实例成员(它可能是一个实例成员属性也可能是一个实例成员对象)。对于类的实例成员,在类的所有实例方法和构造函数中都可以引用它们。 如果GamePage是一个对象,那它肯定已经在某处实例化过了,否则在这里引用它的img属性(或img变量,因为img也可能是一个成员变量)就不对。如果GamePage是一个属性,那它肯定会返回一个已经实例化的对象,且这个对象有一个img成员,这里才能引用这个img成员。

这不是构造函数在对象实例化时自动调用,因为这里没有实例化任何对象。

构造函数在对象实例化时自动调用,是这样的:
class A

public A(int i)

this.i=i;
Console.WriteLine("调用构造函数了");

public int i;

A a1=new A(100);
new A(100)是在实例化类A的一个对象,因此会调用类A的构造函数,输出: 调用构造函数了。
参考技术B

构造函数就是为了初始化类内部的成员变量,新建一个类的时候,会有默认的构造函数,而且是隐藏的,当你手工创建一个构造函数的时候,那默认的构造函数将不复存在

参考技术C 直接给GamePage的img属性赋值是可以的,只要把img声明成静态就行,如果不声明成静态的就会报错。
这个和构造函数在对象实例化时自动调用没关系
参考技术D 这个是GamePage的属性,属性赋值操作不需要实例化。追问

您的意思是 GamePage 是 C# .NET Framwork 里面 系统基础类吗 ?

追答

不是,GamePage是控件名的话,你这样操作就是属性赋值。

:崭露头角,基于Cglib实现含构造函数的类实例化策略

目标

这一章节的目标主要是为了解决上一章节我们埋下的坑,那是什么坑呢?其实就是一个关于 Bean 对象在含有构造函数进行实例化的坑。

在上一章节我们扩充了 Bean 容器的功能,把实例化对象交给容器来统一处理,但在我们实例化对象的代码里并没有考虑对象类是否含构造函数,也就是说如果我们去实例化一个含有构造函数的对象那么就要抛异常了。

怎么验证?其实就是把 UserService 添加一个含入参信息的构造函数就可以,如下:

ublic class UserService 

    private String name;

    public UserService(String name) 
        this.name = name;
      

    // ...

报错如下:

java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService

 at java.lang.Class.newInstance(Class.java:427)
 at cn.bugstack.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
 ...

发生这一现象的主要原因就是因为 beanDefinition.getBeanClass().newInstance(); 实例化方式并没有考虑构造函数的入参,所以就这个坑就在这等着你了!那么我们的目标就很明显了,来把这个坑填平!

设计

填平这个坑的技术设计主要考虑两部分,一个是串流程从哪合理的把构造函数的入参信息传递到实例化操作里,另外一个是怎么去实例化含有构造函数的对象。

  • 参考 Spring Bean 容器源码的实现方式,在 BeanFactory 中添加 Object getBean(String name, Object… args) 接口,这样就可以在获取 Bean 时把构造函数的入参信息传递进去了。
  • 另外一个核心的内容是使用什么方式来创建含有构造函数的 Bean 对象呢?这里有两种方式可以选择,一个是基于 Java 本身自带的方法 DeclaredConstructor,另外一个是使用 Cglib 来动态创建 Bean 对象。Cglib 是基于字节码框架 ASM 实现,所以你也可以直接通过 ASM 操作指令码来创建对象。

实现

工程结构


Spring Bean 容器类

本章节“填坑”主要是在现有工程中添加 InstantiationStrategy 实例化策略接口,以及补充相应的 getBean 入参信息,让外部调用时可以传递构造函数的入参并顺利实例化。

新增 getBean 接口

package com;

/**
 * Bean工厂
 */
public interface BeanFactory 

    Object getBean(String name) throws Exception;

    Object getBean(String name,Object... args) throws Exception;

  • BeanFactory 中我们重载了一个含有入参信息 args 的 getBean 方法,这样就可以方便的传递入参给构造函数实例化了。
package com;

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory 

    @Override
    public Object getBean(String name) throws Exception 
       return doGetBean(name,null);
    

    @Override
    public Object getBean(String name,Object... args) throws Exception 
        return doGetBean(name,args);
    

    public <T> T doGetBean(String name, Object... args) throws Exception 
        Object bean = getSingleton(name);
        if(bean != null)
            return (T)bean;
        
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return (T)createBean(name,beanDefinition,args);
    

    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws Exception;

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws Exception;

定义实例化策略接口

package com;

import java.lang.reflect.Constructor;

public interface InstantiationStrategy 
    Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor,Object[] args) throws Exception;

  • 在实例化接口 instantiate 方法中添加必要的入参信息,包括:beanDefinition、 beanName、ctor、args
  • 其中 Constructor 你可能会有一点陌生,它是 java.lang.reflect 包下的 Constructor 类,里面包含了一些必要的类信息,有这个参数的目的就是为了拿到符合入参信息相对应的构造函数。
  • 而 args 就是一个具体的入参信息了,最终实例化时候会用到。

JDK 实例化

package com;

import java.lang.reflect.Constructor;

public class SimpleInstantiationStrategy implements InstantiationStrategy 
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws Exception 
        Class clazz = beanDefinition.getBeanClass();
        try 
            if(ctor != null)
                return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
            else
                return clazz.getDeclaredConstructor().newInstance();
            
        catch (Exception e)
            throw new RuntimeException("Failed to instantiate [" + clazz.getName() + "]", e);
        
    

  • 首先通过 beanDefinition 获取 Class 信息,这个 Class 信息是在 Bean 定义的时候传递进去的。
  • 接下来判断 ctor 是否为空,如果为空则是无构造函数实例化,否则就是需要有构造函数的实例化。
  • 这里我们重点关注有构造函数的实例化,实例化方式为 clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);,把入参信息传递给 newInstance 进行实例化。

Cglib 实例化

package com;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;

import java.lang.reflect.Constructor;

public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy 
    @Override
    public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws Exception 
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(beanDefinition.getBeanClass());
        enhancer.setCallback(new NoOp() 
            @Override
            public int hashCode() 
                return super.hashCode();
            
        );
        if(ctor == null)
            return enhancer.create();
        
        return enhancer.create(ctor.getParameterTypes(),args);
    

  • 其实 Cglib 创建有构造函数的 Bean 也非常方便,在这里我们更加简化的处理了,如果你阅读 Spring 源码还会看到 CallbackFilter 等实现,不过我们目前的方式并不会影响创建。

创建策略调用

package com;

import java.lang.reflect.Constructor;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory 

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String name, BeanDefinition beanDefinition,Object[] args) throws Exception 
        Object bean = null;
        try 
           bean = createBeanInstance(beanDefinition,name,args);
        catch (Exception e)
           throw new RuntimeException("Instantiation of bean failed");
        
        addSingleton(name,bean);
        return bean;
    

    /**
     * 查找对应构造函数
     * @param beanDefinition
     * @param beanName
     * @param args
     * @return
     * @throws Exception
     */
    protected Object createBeanInstance(BeanDefinition beanDefinition,String beanName,Object[] args) throws Exception
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for(Constructor ctor : declaredConstructors)
           if(args != null && ctor.getParameterTypes().length == args.length)
               constructorToUse = ctor;
               break;
           
        
        return getInstantiationStrategy().instantiate(beanDefinition,beanName,constructorToUse,args);
    

    public InstantiationStrategy getInstantiationStrategy() 
        return instantiationStrategy;
    

  • 首先在 AbstractAutowireCapableBeanFactory 抽象类中定义了一个创建对象的实例化策略属性类 InstantiationStrategy instantiationStrategy,这里我们选择了 Cglib 的实现类。
  • 接下来抽取 createBeanInstance 方法,在这个方法中需要注意 Constructor 代表了你有多少个构造函数,通过 beanClass.getDeclaredConstructors() 方式可以获取到你所有的构造函数,是一个集合。
  • 接下来就需要循环比对出构造函数集合与入参信息 args 的匹配情况,这里我们对比的方式比较简单,只是一个数量对比,而实际 Spring 源码中还需要比对入参类型,否则相同数量不同入参类型的情况,就会抛异常了。

测试

package com;

public class UserService 

    private String name;

    public UserService() 
    

    public UserService(String name) 
        this.name = name;
    

    public void queryUserInfo()
        System.out.println("查询用户信息:"+name);
    

package com;

import org.junit.Test;

public class UTest

    @Test
    public void test_BeanFactory() throws Exception 
         //1.初始化 BeanFactory
         DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
         //2.注冊 Bean
         BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
         beanFactory.registerBeanDefinition("userService",beanDefinition);
         //3.获取 Bean
         UserService userService = (UserService)beanFactory.getBean("userService","小张");
         userService.queryUserInfo();
    

以上是关于C# 关于 构造函数问题 关于对象实例化的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript--关于实例对象带不带参数和构造函数带不带参数的关系

:崭露头角,基于Cglib实现含构造函数的类实例化策略

c#中对象初始化是啥意思?

使用 COM 的参数化构造函数实例化类

javascript原型继承

如何访问类的私有构造函数?