1

I am implementing an auto mapper and stuck at this kind of scenario:

  1. There is object of class common inside the object of class source which is just declared but not instantiated
  2. when i pass the object of class source to a method map of program class

how can i get type of inner object(common class object) and instantiate it?

public class common
{
    public int x;
}

public class source
{
    public common obj;
}

class program
{
    static void main()
    {
        source obj = new source();
        map(obj);
    }

    void map(source obj)
    {
        **how can i get type of inner object class and instantiate it**
    }
}

i used Type.GetType() but is giving empty.

murali.j
  • 53
  • 6
  • Please format your code properly – JosephGarrone Jan 27 '17 at 09:41
  • 1
    Even if you get the type, you can't set the fields because they are private. Why would you want to know the type though? You already know about `source` which means you already know that the `obj` field is of type `common`. What are you really trying to do? – Panagiotis Kanavos Jan 27 '17 at 09:41
  • 1
    How about using generics and restrict the type to class, new() ? You could then use default() to create a new instance. – Dennis Kuypers Jan 27 '17 at 09:43
  • Surely you can just make common obj public? Are you unable to change source for some reason? – Tim Rutter Jan 27 '17 at 09:47
  • @PanagiotisKanavos sorry for not mentioning assume they are public. actually the objects are dynamically passed in my code for auto mapping objects of two classes where i dont know the type of internal object. only objects of outer classes are passed to map them – murali.j Jan 27 '17 at 09:48
  • 1
    @murali.j instead of assuming, just clean up the code and explain what you want *in the question itself*. As it is, the question text is misleading – Panagiotis Kanavos Jan 27 '17 at 09:50
  • Honestly did you even attempt to compile and run this program? Even after the recent changes it won't run because `main()` should be `Main()` –  Jan 27 '17 at 09:55
  • @MickyD i already mentioned above this is just to show the scenario i showed as i cannot show the complete code. this is actually implementation of auto mapper – murali.j Jan 27 '17 at 10:06
  • I'm not talking about _"complete code"_. I'm talking about the fact that you don't even define a `Main()` method properly. Now if you said _"whoops! typo!"_ all would be forgiven, but it's obvious you haven't done any testing at all. Along with **most of the authors of answers on this page** –  Jan 27 '17 at 10:10
  • @MickyD that happens because everyone just wants to convey Q/A. – murali.j Jan 27 '17 at 10:22

6 Answers6

1

So working implementation of map method:

common map(source obj)
{
    var typeOfObj = obj.GetType().GetFields(
        BindingFlags.Public |
        BindingFlags.NonPublic |
        BindingFlags.Instance)
        .First(fi=>fi.Name == "obj");
    return (common) Activator.CreateInstance(typeOfObj.FieldType);
}

ARCHIVED If you need get type for obj.obj then you have use:

void map(source obj){
   obj.obj.GetType() ...
}

But since you have not instantiate it yet then you need reflection:

var f = typeof(source).GetField("obj");
f.FieldType // contains expected

If private field is a problem then you have to iterate by all array of fields resolved by:

FieldInfo[] fields = source.GetFields(
                     BindingFlags.NonPublic | 
                     BindingFlags.Instance);

And locate field named "obj"

Dewfy
  • 23,277
  • 13
  • 73
  • 121
  • 3
    1) private fields 2) When you already know the `source` type you know that `source.obj` is of type common. Why ask for it again? – Panagiotis Kanavos Jan 27 '17 at 09:43
  • He means, that obj.obj is null, your code will run in nullpointerexception, that was his question, how to avoid it. Answer: iterate throur GetProperties() – Maksim Simkin Jan 27 '17 at 09:44
  • The first code example results in a null ref exception. The second, the call to `GetField` results in `null` –  Jan 27 '17 at 09:52
  • @MickyD that is why I post 3'd code-block to explore fields regardless of `private` accessor – Dewfy Jan 27 '17 at 10:07
  • Which is a jolly good one, but if you fix the other two I'd give you a +1 too :) –  Jan 27 '17 at 10:12
1

And if you want to instantiate your received class you can use Activator:

Activator.CreateInstance(obj.obj.GetType());

Although I'm not sure what you are trying to achieve.

Paweł Swajdo
  • 391
  • 1
  • 13
1

correct my understanding if I am wrong.

you want to instantiate the inner common object once you instantiated the source object. if so you could write an constructor inside your source which will instantiate your private common object. something like this

class source
{ 
    common obj;
    public source()
    {
        obj = new common();
    }

}
  • How does this address _"I am implementing an auto mapper"_? –  Jan 27 '17 at 09:56
  • @ahamed-ali But i am passing object of source to another class and till then i wont create object of common class. But for the scenario you understood, thats a good approach – murali.j Jan 27 '17 at 13:36
0

Where is the problem on this? No need for reflection:

void map(source obj)
{
    obj.obj = new common();
}

Basically you can modify any passed object as you want, the only thing you can´t do within your method is to set obj to a new instance of source for which you´d need the ref-keyword. In your case you know the type of the passed object, so I guess you also know what the concerning class consists of and thus the type for the obj-property.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
0

Get type field info from your input object then instantiate the object of that field's type and set the value back to your input object :

// Get field info for source.obj
FieldInfo fieldInfo = obj.GetType().GetField("obj", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
// Instantiate object without initializing it
object instance = FormatterServices.GetUninitializedObject(fieldInfo.FieldType);
// set instantiated object reference to the field source.obj
fieldInfo.SetValue(obj, instance);

If you're making "AutoMapper" then this will fit your needs because it doesnt use constructor which in other answers should be the default ( parameterless ) one.

Here you can compare different methods of instantiating object without default ( parameterless ) constructor.

Full code example that will map recursively :

public static void Main()
{
    _genericMapInfo = typeof(Program).GetMethod("map", BindingFlags.NonPublic | BindingFlags.Static);
    Test t = null;
    map(ref t);
    Console.WriteLine(t == null); // False
    Console.WriteLine(t.A); // Hello World!
    Console.WriteLine(t.B.A); // 1337
    Console.WriteLine(t.B.B); // Hello World!
}

static MethodInfo _genericMapInfo;

static void map<T>(ref T obj)
    where T : class
{
    if(obj == null)
        obj = (T)FormatterServices.GetUninitializedObject(typeof(T));

    foreach(FieldInfo fInfo in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
    {
        Type fieldType = fInfo.FieldType;   
        TypeCode tCode = Type.GetTypeCode(fInfo.FieldType);
        if(tCode == TypeCode.Object)
        {
            object[] param = new object[] { fInfo.GetValue(obj) };
            _genericMapInfo.MakeGenericMethod(fieldType).Invoke(null, param);
            fInfo.SetValue(obj, param[0]);
        }
        else if(tCode == TypeCode.String)
        {
            fInfo.SetValue(obj, "Hello World!");    
        }
        else if(tCode == TypeCode.Int32)
        {
            fInfo.SetValue(obj, 1337);  
        }

    }
}

public class Test
{
    public string A { get; set; }   
    public TestInside B { get; private set; }

    public Test()
    {
        A = "no siema"; 
    }
}

public class TestInside
{
    public int A { get; private set; }
    public string B { get; set; }   

    public TestInside(int _someInteger)
    {
        A = _someInteger;   
    }
}

Online check

mrogal.ski
  • 5,828
  • 1
  • 21
  • 30
  • 1
    Your line should read `FieldInfo fieldInfo = obj.GetType().GetField("obj", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);`. Fix it and I'll +1 you –  Jan 27 '17 at 09:58
  • @MickyD Thank you for the hint. Edited it – mrogal.ski Jan 27 '17 at 10:00
  • Well done! First answer that actually works and addresses OP's concerns! +1 –  Jan 27 '17 at 10:01
0

we can get the type by using

.GetType()

method but ofcourse my case is different since i stored all fields in

FieldInfo[]

so i used

FieldInfo.FieldType

to get the type of my object. Once we had our object type we can use

Activator.CreateInstance(Class Type);

to create the instance of class.

murali.j
  • 53
  • 6