namespace MyOldService
{
public MyNewService.AddressList ToPrivateAddressList()
{
MyNewService.AddressList privAddrList = new MyNewService.AddressList();
privAddrList.creator = (MyNewService.AddressListCreator)this.creator;
privAddrList.listId = this.listId;
privAddrList.listIdSpecified = this.listIdSpecified;
privAddrList.listName = this.listName;
privAddrList.listType = (MyNewService.AddressingMode)this.listType;
privAddrList.lastModified = this.lastModified;
privAddrList.lastModifiedSpecified = this.lastModifiedFieldSpecified;
if (this.siteList != null && this.listType == MyOldService.AddressingMode.XDAddressingModeSiteIDList)
{
privAddrList.siteList = new long[this.siteList.Length];
Array.Copy(this.siteList, privAddrList.siteList, this.siteList.Length);
}
...
Originally written to copy a list defined in a SOAP namespace MyOldService to a class of the same layout in a new namespace MyNewService. The problem is, with soap classes, if I import the MyOldService namespace into a third namespace, say MyOtherAppService, then my AddressList class becomes a member of that third namespace and is referenced as such.
So, rather than duplicating the code, I'd like to decorate it (or adjust it somehow) with something akin to generics (which I understand won't work because I'm altering the namespace, not just one fixed type [there are multiple types I need from each namespace, as can be seen from the snippet]) to allow this to convert the address list to the corresponding class in whichever namespace is needed. Possible?
Edit: In response to some of the comments below, I'll try to define a better example of what I'm trying to do.
Three classes imported from WSDLs via web references (no, these won't compile, just examples for illustration). The classes AddressList all have the same layout.
namespace A
{
enum Mode {};
enum Creator {};
class ATypeClass {}
public partial class AddressList
{
int id;
enum Mode mode;
enum Creator creator
long[] siteList;
ATypeClass[] cspList;
}
}
namespace B
{
enum Mode {};
enum Creator {};
class BTypeClass {}
public partial class AddressList
{
int id;
enum Mode mode;
enum Creator creator
long[] siteList;
BTypeClass[] cspList;
}
}
namespace C
{
enum Mode {};
enum Creator {};
class CTypeClass {}
public partial class AddressList
{
int id;
string name;
enum Mode mode;
enum Creator creator
long[] siteList;
CTypeClass[] cspList;
}
}
I'll extend the partial class in namespace A with a new method:
namespace A
{
public partial class AddressList
{
public T.AddressList ToPrivateAddressList<T>()
{
T.AddressList privAddrList = new T.AddressList();
privAddrList.creator = (T.Creator)this.creator;
privAddrList.id = this.id;
privAddrList.name = this.name;
privAddrList.mode = (T.Mode)this.mode;
if (this.siteList != null && this.listType == Mode.XDAddressingModeSiteIDList)
{
privAddrList.siteList = new long[this.siteList.Length];
Array.Copy(this.siteList, privAddrList.siteList, this.siteList.Length);
}
...
}
}
}
Notice that part of the problem, in addition to the classes each part of a different namespace, are the enums that are also from the varying namespaces.
Finally, I envision calling it like so (though I know I can't actually do this, I'm looking for a solution that's roughly as elegant):
B.AddressList al1 = A.AddressList.ToPrivateAddressList<B>();
C.AddressList al1 = A.AddressList.ToPrivateAddressList<C>();
I think what you are looking for is an interface and the where keyword in the generic type definition.
From your code I see that you have this:
a method that converts type A to type B
it does this by assigning properties with the same name
and then returns the new type B
the types can reside in different namespaces (but currently have the same name)
classes are partial
Instead of relying on the same name (which you can, using reflection you can achieve the effect you want), you should let each class implement an interface that contains the common properties. This way, you retain compile time type-safety:
public interface ICommonAddress
{
int id { get; set; }
Mode mode { get; set; }
Creator creator { get; set; }
long[] siteList { get; set; }
ICommonAddress CreateAddress();
}
You can now refactor your classes like this (of course, you'll have to change your fields into properties, but I'm assuming you have them as properties already:
// if your original partial class is auto-generated, it is ok to place
// this definition in another file, it'll still work as long as
// namespace, classname and compile-unit (must be in same project) are the same
public partial class AddressList : ICommonAddress
{
int id { get; set; }
Mode mode { get; set; }
Creator creator { get; set; }
long[] siteList { get; set; }
ATypeClass[] cspList;
ICommonAddress CreateAddress()
{
return new AddressList(); // NOTE: you can even have your ctor private!
}
}
If you do that for each AddressList type you have, you can change your generic method as follows, and it will automatically work, including the IntelliSense showing you the common available properties. Also, implement it as an extension method, so that it applies to all your AddressList types (this is in your case better than using partial):
public T ToPrivateAddressList<T>(this ICommonAddress _this)
where T: ICommonAddress
{
T privAddrList = _this.CreateAddress();
// this now works normally, without casting
privAddrList.creator = _this.creator;
privAddrList.id = _this.id;
privAddrList.name = _this.name;
privAddrList.mode = _this.mode;
}
Now, if you import a reference to this extension method, you can call ToPrivateAddressList() on any type that you have:
A.AddressList a_address = A.AddressList.CreateAddress(); // or new A.AddressList()
B.AddressList al1 = a_address.ToPrivateAddressList<B.AddressList>();
C.AddressList al1 = a_address.AddressList.ToPrivateAddressList<C.AddressList>();
If I right undesrtood your problem you have
namespace A
{
public class MyClass {... }
}
and
namepsace B
{
public class MyClass {...}
}
Your question is: how can I define something "generic" that in the same function sometimes deals with A.MyClass and other cases B.MyClass.
If you think about namespaces like a part of the type definition, I think the story becomes clear.
Like if you have 2 different types and want to have one generic that works with both of them. That generic class knows when to choose A.MyClass method, and when to choose B.MyClass method.
If it's not what you're asking for, please clarify.
Are you asking how you can do this:
namespace Old
{
public New.Address Foo()
{
New.Address result;
//...
return result;
}
}
Without having to say 'New.' on each occurrence of Address? But if you don't, the code uses Old.Address?
Can you remove Old.Address from the build/linking of this project (with this code)?
Related
I have classes that might or might not change their name (and members) during development. My classes are used (in most cases) like enums, but I couldn't use enums because I needed slightly more functionality. Since classes (obviously) don't have an Integer representing them under the surface I need to create some solution for having similar functionality. In other words, I want for each class to be represented by an Integer (or some other unique identifier).
I've created this attribute:
public class IdAttribute : Attribute
{
private int id = -1;
public IdAttribute(int index)
{
this.id = index;
}
public int Id
{
get
{
return id;
}
}
}
And I'm using it as following:
[Id(0)]
public class Hello: Core { }
[Id(1)]
public class Bye: Core { }
As you can see it's quite error prone, since I don't want any class to have the same Id. And thus, optimally I want an automatic generated id, but I don't want it to change if I ever change anything regarding the class, for example the class name or its members.
What's the best way to achieve this?
(I know that in Java, that once you make a class Serializable, you'll get an automatically generated id (is there something like this in C#?).)
EDIT:
The reason I "couldn't" just use enums is because of (mainly) convenience. I have classes which exposes fields in an editor. And in this editor I can select only the appropriate "enums", in some cases only enums which inherits from "Core" will be displayed and in other cases they might inherit from "Tools" or some other class. I hope that cleared up a bit.
Not sure why you'd need to do this, but you could do the following:
[AttributeUsage(AttributeTargets.Class)]
public class IdAttribute:Attribute
{
public Guid Id { get; }
public IdAttribute(string id)
{
Id = new Guid(id);
}
}
And you'd use it like:
[IdAttribute("7d7952d1-86df-4e2e-b040-fed335aad775")]
public class SomeClass
{
//example, you'd obviously cache this
public Guid Id => GetType().GetCustomAttribute<IdAttribute>().Id;
//...
}
Do note, that Guids are not random. If you need a random id, then this isn't the solution. To generate a Guid read comments to your question.
You can handle that through your base class Core:
public abstract class Core
{
public Core()
{
Type myType = this.GetType();
object[] attrs = myType.GetCustomAttributes(typeof(IdAttribute), false);
IdAttribute attr = attrs?.OfType<IdAttribute>().FirstOrDefault();
int id = -1;
if (attr != null) id = attr.Id;
if (!reservedIdentities.ContainsKey(id))
{
reservedIdentities.Add(id, myType);
}
else
{
if (!reservedIdentities[id].Equals(myType))
throw new ArgumentException("Duplicate identities discovered.", nameof(id));
}
}
static Dictionary<int, Type> reservedIdentities = new Dictionary<int, Type>();
//...
}
I have this piece of code
public class Ticket
{
public string strArticleID { get; set; }
public string strArticleDescription { get; set; }
public decimal decArticlePrice { get; set; }
public decimal decArticleVAT { get; set; }
public decimal decArticuleNetPrice { get; set; }
public decimal decArticleDiscount { get; set; }
public decimal decArticleQuantity { get; set; }
}
public static List<Ticket> _lstCurrentTicket = new List<Ticket>();
That I want so send to an external DLL to get all the lines in _lstCurrentTicket to print a ticket through
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
Ticket ticket = new Ticket();
string strRefID = this.dataGridView1.Rows[i].Cells[0].Value.ToString();
string strDescription = this.dataGridView1.Rows[i].Cells[1].Value.ToString();
decimal decQuantity = (decimal)this.dataGridView1.Rows[i].Cells[2].Value;
decimal decUPrice = (decimal)this.dataGridView1.Rows[i].Cells[3].Value;
decimal decDiscount = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[4].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[4].Value.ToString().Length - 1));
decimal decVAT = Convert.ToDecimal(this.dataGridView1.Rows[i].Cells[5].Value.ToString().Substring(0, this.dataGridView1.Rows[i].Cells[5].Value.ToString().Length - 1));
decimal decGPrice = (decimal)this.dataGridView1.Rows[i].Cells[6].Value;
ticket.strArticleID = strRefID;
ticket.strArticleDescription = strDescription;
ticket.decArticlePrice = decUPrice;
ticket.decArticleVAT = decVAT;
ticket.decArticuleNetPrice = decGPrice;
ticket.decArticleDiscount = decDiscount;
ticket.decArticleQuantity = decQuantity;
_lstCurrentTicket.Add(ticket);
}
TicketPrinting tktPrint = new TicketPrinting ();
//Ticket and copies
tktPrint.PrintTicketFromList(_lstCurrentTicket, 2);
Since it is an external DLL, I thought the easiest way to work with it in target DLL was
public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
{
foreach (var prop in lstArticles.GetType().GetProperties())
{
if (prop.Name == "Item")
{
//Copy external list to local class for printing
}
}...
But I'm stuck there. How can I iterate each property and value from each original class in the list so I can copy it? If I make a breakpoint I can see that the fields and values are correctly passed, but I do not get how to access them so I can do something like creating a local class exactly like the original and clone the list (and if I try it will say local list(Ticket) and passed List(T) are not the same type).
Or how could I copy it if I create an exact class in the target and do something like
public void PrintTicketFromList(object lstArticles, short intCopies)
{
List<TargetDLLTicket> lst =((List<TargetDLLTicket>)lstArticles).ToList(); }
Any thoughts?
It sounds like you have a circular dependency issue. You need to move the types you are sending to your print function to a common assembly (new project) that is then referenced by both the calling project and your print project. Then both projects can access this shared type.
A note about your design. The way you are going about this is probably not good to begin with thus your error. The actual printer function should not have to know anything about the types passed in. A good rule of thumb is to try to make your code as loosly coupled as possible. A better idea is to create an Interface that takes care of writing to the printer canvas (or something like that, you did not provide your printer code so this is a guess) and the printer function can call that method on the incoming object. The printer method should then also only accept that interface as a parameter. This is based on a Visitor pattern. Here is an example.
public interface IPrintable {
void WriteToPrinter(PrinterCanvas canvas);
}
public class Printer {
public void Print(IPrintable somethingToPrint) {
var canvas = getCanvas();
somethingToPrint.WriteToPrinter(canvas);
}
}
If at any point possible you should try to avoid reflection like Igor does in his answer.
But if you really want to use reflection you are currently not inspecting the item but the list of items.
You should try something like (writing this from memory):
public void PrintTicketFromList<T>(List<T> lstArticles, short intCopies)
{
foreach (var item in lstArticles)
{
foreach (var prop in typeof(T).GetProperties())
{
var value = prop.getValue(item);
}
}
}
Instead of List<T> create an interface, ITicket for example and accept List<ITicket>. Using List<T> as a generic whenever you know you only can work with something that is a Ticket is creating an unnecessary wide range of potential inputs. Using an interface allows you to not worry about the concrete implementation, and instead get at only what your dll is concerned with, the contract.
You could put the interface in one of two places, either another external common assembly that both of your assemblies reference, or you could put the interface into your assembly that has the ticket printing logic. Your Ticket class could then implement the interface.
An example of what this could look like:
public interface ITicket
{
//properties and methods you want to have all implementations to contain.
}
public class Ticket : ITicket
{
}
public class LastTicket :ITicket
{
}
public void PrintTicketFromList(List<ITicket> lstArticles, short intCopies)
{
}
I have a large collection of automatically generated objects. Although they are all of different, non-related classes, all of the objects share some basic properties (name, id, etc.). I do not control the generation of these objects, so unfortunately I cannot take the ideal approach of implementing an interface. I would like to create a method in which I pass an arbitrary one of these objects and do something using these common properties.
The general idea would be something like:
someObj a = new someObj();
a.name = "sara";
diffObj b = new diffObj();
b.name = "joe";
string phrase = string.Format("I am with {0} and {1}",
getName(a), getName(b));
private string getName(object anyObjWithName)
{
return anyObjWithName.name;
}
though naturally this does not work.
I thought a generic method might hold the answer, but the only way I can see to call it with the current type is using genericMethod.Invoke , which still carries the same issue of not being able to resolve the properties of the passed object in the method. This is unlike Calling generic method with a type argument known only at execution time or How to call generic method with a given Type object? where only the type, or properties of the type, are used in the method, as opposed to properties of the object.
I am aware that this would be (very) prone to error, but I can guarantee that all objects encountered will have the common properties being manipulated.
I can guarantee that all objects encountered will have the common properties being manipulated
If that's the case, you can use dynamic:
private string getName(dynamic anyObjWithName)
{
return anyObjWithName.name;
}
Be aware that using any object that does not have a name property will not fail until run-time.
If you want to add a little bit of safety you can catch the RuntimeBinderException that gets thrown if the property does not exist:
private string getName(dynamic anyObjWithName)
{
try {
return anyObjWithName.name;
}
catch(RuntimeBinderException) {
return "{unknown}";
}
}
If you're unhappy with the performance using dynamic as mentioned by D Stanley, you could always try FastMember.
All you need to know to start using it is pretty much shown in the first 2 code examples.
You are creating a Rube Goldberg device there. You should just have all your data objects classes implement a single interface, then you can work on that. Much simpler and less error prone than fiddling with reflection.
The very fact that a lot of objects have common properties but don't share the same ancestry, on in the very least a common interface, shows that something is wrong with your design. Do rethink it.
Multiple ways to accomplish this, simplest probably is to create Interface and declare common methods there, have your object implement it, then change "getName" method take interface object
private string getName(IMyInterface anyObjWithName)
{
return anyObjWithName.name;
}
The correct way to do this is with an interface, if you own the types that you're working with
public interface IEntity
{
int ID { get; set; }
string Name { get; set; }
}
public class TypeOne : IEntity
{
public int ID { get; set; }
public string Name { get; set }
public string BespokePropertyOne { get; set;}
}
public class TypeTwo : IEntity
{
public int ID { get; set; }
public string Name { get; set; }
public float BespokePropertyTwo { get; set; }
}
static void Main(string[] args)
{
List<IEntity> entities = new List<IEntity>();
entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" });
entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f });
foreach (IEntity entity in entities)
{
Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name);
}
}
This answer was written before the edit to the question stating that interfaces weren't possible in this case. Perhaps it can help someone else reading this question.
Interface:
interface Iname
{
string Name { get; set; }
}
Use interface:
class A : Iname
{
public string Name { get; set; }
}
class B : Iname
{
public string Name { get; set; }
}
The method:
string GetName(Iname o)
{
return o.Name;
}
Use:
A a = new A { Name = "First" };
B b = new B { Name = "Last" };
Text = GetName(a) + " " + GetName(b);
I have 10 tables which are presenting to LINQ as different types, but share exactly the same properties. When I try to run a union on them, the compiler tells me that:
"Argument 2: cannot convert from 'System.Collections.IEnumerable' to 'System.Collections.Generic.IEnumerable<LINQPad.User.TelJun2011>'"
The code looks like this
var jul = (from n in TelJul2011s select n);
var jun = (from p in TelJun2011s select p);
jun.Union(jul).Dump();
I've done my research and understand that unions cannot be performed across different types, I also understand that unions can be performed on anonymous types if they share the same properties. This option will not work for me, as I need all the properties from all the tables and don't want to have to type out the same anonymous type 10 times - once for each variable. I want the compiler to infer that they are all the same type based on the fact that all properties are identical.
I've already tried casting to an IEnumberable, Iqueryable, Datatable etc. using both the AsQueryable() type functions, and the "as" keyword. None of that seems to do the trick for me.
I'm wondering if there is some way of doing this by casting dynamically to a parent type. I can't edit the initial declarations of the classes, so can't implement a common interface on them to cast to. But is there some way I can downcast the types into a common interface when they are used, without writing a conversion from each type to a parent interface?
Thanks for any advice!
The result of the Union will be an IEnumerable<Xxx> but in this case you have to specify what Xxx is. If the types TelJun2011 and TelJul2011 are not structs (value-types), you can utilize the covariance of IEnumerable<out T> like this:
jun.Union<object>(jul)
This works because TelJun2011 and TelJul2011 are both object, and then by covariance IEnumerable<TelJun2011> and IEnumerable<TelJul2011> are both IEnumerable<object>.
Of course object does not possess all the properties common to TelJun2011 and TelJul2011. It would be better if these two types had a more useful common base class or implemened a common interface, because then you could say e.g.:
jun.Union<ITelMonth>(jul)
where ITelMonth were some type containing the common properties you want.
The "problem" is that C#'s static typesystem won't allow what you're trying to achieve. You will have to cast the objects to a base type before union-ing them. That means if both have just System.Object in common, you would have to cast it either to object or (for .net 4.0) to dynamic.
The latter will force a dynamic typechecking:
class A
{
public int Integer { get; set; }
}
class B
{
public int Integer { get; set; }
}
class Program
{
public static void main(string[] args)
{
var a = new[] { new A { Integer = 5 }, new A { Integer = 6 }, new A { Integer = 7 } };
var b = new[] { new B { Integer = 1 }, new B { Integer = 2 }, new B { Integer = 3 } };
var u = a.Cast<dynamic>().Union(b).ToArray();
var i1 = u[0].Integer;
var i2 = u[1].Integer;
var i3 = u[2].Integer;
var i4 = u[3].Integer;
var i5 = u[4].Integer;
var i6 = u[5].Integer;
}
}
In my opinion this is not an ideal solution, but this might help you.
If you're using EntityFramework (or any other framework, I guess), the classes auto-generated are marked as partial, such as the following:
/Project/Data/TelJun2011.cs
namespace Project.Data
{
using System;
using System.Collections.Generic;
public partial class TelJun2011
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
/Project/Data/TelJul2011.cs
namespace Project.Data
{
using System;
using System.Collections.Generic;
public partial class TelJul2011
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
What partial means is that you can create another file for the same class. The generated classes don't implement an interface, but you can easily make then implement your custom interface like this:
/Project/Data/ITelMonth.cs
namespace Project.Data
{
public interface ITelMonth
{
int Id { get; set; }
string Name { get; set; }
string Description { get; set; }
}
}
/Project/Data/Partial/TelJun2011.cs
namespace Project.Data
{
public partial class TelJun2011 : ITelMonth { }
}
/Project/Data/Partial/TelJul2011.cs
namespace Project.Data
{
public partial class TelJul2011 : ITelMonth { }
}
And having correctly defined the interfaces, then we can simply do this:
var jul = (from n in TelJul2011s select (ITelMonth)n);
var jun = (from p in TelJun2011s select (ITelMonth)p);
var bimester = jun.Union(jul);
And you can even access the common properties like such:
foreach (var e in bimester)
{
e.Id.Dump();
}
After searching 99% of the net I am still stuck on the following matter. I have a web service which must comply to a wsdl that a partner company supplied. Calling a method of this service results in a (complex) class. Unfortunately a serialization error is raised when the service is called.
I have pinpointed the issue but cannot think of (and find) a solution to it. Because I'm dependant on the wsdl which was supplied, I cannot change the complex class structure. Hope anyone knows what I am missing. Here is example code to reproduce my issue:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
public object Item { get; set; } // <---- Note type *object* here
}
[System.SerializableAttribute()]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
[TestClass]
public class SerializationTest
{
[TestMethod]
public void Serializing()
{
MyObject myObject = new MyObject { Id = 1 };
myObject.Item = new MyItem[] { new MyItem { Id = 1, Name = "Test" } };
string serializedString = SerializeObjectToXmlString(myObject, new []{ typeof(MyItem)});
Assert.IsFalse(String.IsNullOrWhiteSpace(serializedString));
}
/// <summary>
/// This method serializes objects to an XML string using the XmlSerializer
/// </summary>
private static string SerializeObjectToXmlString(object theObject, Type[] types)
{
using (var oStream = new System.IO.MemoryStream())
{
var oSerializer = new System.Xml.Serialization.XmlSerializer(theObject.GetType(), types);
oSerializer.Serialize(oStream, theObject); // <- Here the error is raised
return System.Text.Encoding.Default.GetString(oStream.ToArray());
}
}
}
In the Try/Catch an error is raised after calling method Serialize(). Details of this error are:
InvalidOperationException was unhandled by user code
- There was an error generating the XML document.
The type MyItem[] may not be used in this context.
My development context consists of Visual Studio 2010, .Net Framework 3.5.
Edit #1: Added Serialization attributes but the error remaines
It appears that you cannot add an array of types to an object and serialize it. The solution was to create a container class which - like the name says - contains the array. This way you can assign the container class to the object and serialize it all.
In addition to my case, I was mislead by the object model created by the wsdl.exe utility, since the container class is only a technical solution to add an array to an object. This container class was also created so everything was already there to use. Only after trying out my custom container class I noticed the already created container class. Lost a lot of time on this matter unfortunately...
You should mark you classes as
[Serializable]
public class MyObject
{
public int Id { get; set; }
public MyItem[] Item { get; set; } // <---- Note type *object* here
}
[Serializable]
public class MyItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Serialize uknown object (Item of MyObject class) you will need to do manually by implementing proper interfaces:
ISerializable and IDeserializationCallback, botha added to MyObject class.
This is an old question, but I had the same problem and found a different solution, so I thought I'd share in case it helps someone else.
I found that I could add attributes to allow arrays of specific types. For the problem above, the MyObject class could be edited as below:
[System.SerializableAttribute()]
public class MyObject
{
public int Id { get; set; }
[XmlElement(Type = typeof(object), ElementName = "Item"), //added
XmlElement(Type = typeof(MyItem[]), ElementName = "Item_asArrayOfMyItem")] //added
public object Item { get; set; } // <---- Note type *object* here
}
Anything that serialized before will still look the same, but now MyObject can be serialized even when Item has type MyItem[], as in the question's test case.