Laden...

Generics: Für Constraint "where T : class" auch Nullable<T> erlauben

Erstellt von TiltonJH vor 12 Jahren Letzter Beitrag vor 12 Jahren 1.848 Views
Hinweis von winSharp93 vor 12 Jahren

Abgetrennt von Nullable für Referenztypen

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren
Generics: Für Constraint "where T : class" auch Nullable<T> erlauben

Hallo,

ich habe folgendes Problem:


public class Foo
{

	[...]

	public void Add<T>(T item)
		where T : class //damit item auch null sein kann
	{
		[...]
	}

	[...]

}

public static class Program
{
	public void Main(params string[] args)
	{
		var foo = new Foo();
		foo.Add<SomeClass>(new SomeClass());
		foo.Add<int?>(4);
		foo.Add<int?>(null);
	}
}

Nun ist Nullable<T> nun mal ein struct, kann selbst also nicht null sein.

Fehlermeldung:
The type 'int?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Foo.Add<T>(T item)'.

Mein Lösungsansatz: eine eigene Klasse nach dem Vorbild von Nullable bauen.
Irgendwie glaube ich, dass das auch nicht zum Ziel führt.

Vorschläge, eure Gedanken?

MfG

Tilton

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

G
75 Beiträge seit 2007
vor 12 Jahren

Moin,

foo.Add<int?>(new int?(4));

geht das auch nicht?

Ciao:
GG 😉

Ciao:
GG 😉

T
156 Beiträge seit 2010
vor 12 Jahren

Dumme Frage,
aber warum lässt Du das Constraint nicht ganz weg? Wenn eh alles als item übergeben werden kann. Allerdings sehe ich in diesem Konstrukt gar keinen Sinn, in Sachen Typsicherheit.
LG, Marko

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren

Hallo,

na ja das ganze Konstrukt ist auch deutlich komplexer. Das Constraint hab ich mittlerweile auch schon ausgebaut.
Sinn war egl. das innerhalb der Klasse Foo Add<T>(null); schreiben kann. Hab das jetzt durch Add<T>(default(T)) ersetzt.

Die Aufgabe des Constraint war egl. eben nur vollwertige "Nullable"s überhaupt zu gelassen sind.
Ohne dieses Constraint könnte Jimmy jetzt auch Werte-Typen verwenden, was der gesammten Hilfsklasse den Sinn nimmt.

MfG

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

1.378 Beiträge seit 2006
vor 12 Jahren

Dumme Frage,
aber warum lässt Du das Constraint nicht ganz weg? Wenn eh alles als item übergeben werden kann. Allerdings sehe ich in diesem Konstrukt gar keinen Sinn, in Sachen Typsicherheit.
LG, Marko

Stimmt, du willst einschränken aber doch alles zulassen. Ohne Constraint funktionierts wunderbar:


        static void Main(string[] args)
        {
            Foo(5);
            Foo((int?)5);
            Foo((int?)null);
            Foo("bar");
            Foo((string)null);
        }

        static void Foo<T>(T item)
        {
            if (ReferenceEquals(item, null))
                Console.WriteLine("Item is null");
            else Console.WriteLine(item);
        }

Oder willst du was anderes erreichen?

Lg, XXX

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren

Ich wollte alle NICHT-Nullable<T> Werte-Typen verbieten, sprich erzwingen das man bspw. int? nutzen muss und int nicht.

EDIT: Gleichzeitig aber auch alle normalen Klassen.

MfG

Tilton

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

1.378 Beiträge seit 2006
vor 12 Jahren

Dann wird wohl eine Überladung herhalten müssen. 😃



        static void Main(string[] args)
        {
            //Foo(5); //error
            Foo((int?)5);
            Foo((int?)null);
            Foo("bar");
            Foo((string)null);
        }

        static void Foo<T>(T item) where T:class 
        {
            if (ReferenceEquals(item, null))
                Console.WriteLine("Item is null");
            else Console.WriteLine(item);
        }

        static void Foo<T>(T? item)where T:struct 
        {
            if (ReferenceEquals(item, null))
                Console.WriteLine("Item is null");
            else Console.WriteLine(item);
        }

Lg, XXX

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren

Wäre mgl, ja. Aber mit T wird intern eine Klasse (ich nenn die jetzt mal) Bar<T> angelegt.

🤔

BarStruct<T> : IBar where T : struct { }
BarClass<T> : IBar where T : class { }

🤔

na ja ... mit dem Gedanken muss ich erst ma' bissel schwanger gehn.

Danke

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

1.378 Beiträge seit 2006
vor 12 Jahren

Wenn es ausschließlich intern verwendet wird, kannst du ja auch einfach object verwenden? So wie ich das verstehe, willst du einfach die Handhabung mit dem Generic Constraints einschränken. Das machen die zwei Methoden auch. Wie du die Objekte intern behandelst ist dann wieder eine andere Geschichte.

Lg, XXX

5.742 Beiträge seit 2007
vor 12 Jahren

Ich wollte alle NICHT-Nullable<T> Werte-Typen verbieten[...] Gleichzeitig aber auch alle normalen Klassen.

Dann schreibe das Nullable<T> einfach aus und setze ein Constraint auf struct, also:


public void Add<T>(Nullable<T> item)
  where T : struct
{
   [...]
}
TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren

Der Fehlerteufel

Gleichzeitig aber auch alle normalen Klassen.

-> hätte sein müssen:

Gleichzeitig aber auch alle normalen Klassen erlauben.

Ich seh schon muss etwas mehr ausholen.


internal class Bar<T> : IEnumerable<T> : IBar
{
	[...]
	public void Add(T item) { [...] }
	void IBar.Add(object item)
	{
		if (item is T)
			this.Add((T)item);
		else
			throw new InvalidOperationException("wrong type.");
	}
	Type IBar.BarType { get { return typeof(T); } }
	[...]
}

public interface IBar : IEnumerable
{
	[...]
	void Add(object item);
	Type BarType { get; }
	[...]
}

public class Foo : IEnumerable<IBar>
{
	[...]
	private IList<IBar> bars = new List<IBar>();
	public void AddNewBar<T>()
	{
		this.bars.add(new Bar<T>());
	}
	public void Add(int index, object item)
	{
		this.bars[index].Add(item);
	}
	public Type[] GetBarTypes()
	{
		var array = new Type[this.bars.Count];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = this.bars[i].BarType;
		}
		return array;
	}
	[...]
}

Ich hoffe nun wird es etwas klarer.

Die Idee mit
BarStruct<T> : IBar where T : struct { }
BarClass<T> : IBar where T : class { }

is da dann schon hilfreich und könnte der Weg zu Lösung sein.

MfG

Tilton

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra

1.378 Beiträge seit 2006
vor 12 Jahren

Leider verstehe ich noch immer nicht was du damit bezwecken willst. Ich kann mir nicht vorstellen in welchem Szenario ich eine generische Klasse brauche, die aber ein nicht generisches Interface implementiert.

Du musst darauf nicht eingehen. Ich kann nur nichts weiteres dazu beitragen da ich den Sinn und Zweck deines Vorhabens nicht verstehe.

Lg, XXX

TiltonJH Themenstarter:in
87 Beiträge seit 2006
vor 12 Jahren

Um die Verwirrung hoffentlich aufzulösen, habe ich mal die Klassendiagramme der eigentlichen Implementierung hochgeladen.

Wie man da sehn kann wird aus Foo -> Table und aus Bar -> Column.

Das Konzept is also so ne art DB-Table aber mit .Net-Datentypen (deswegen auch kein DataTable).

Jede Spalte hat ihren eigen Datentyp. Man könnte auch einfach alles ohne generic lösen, aber dann müsste man sich irgendwie anders drum kümmern, dass jede Spalte nur mit dem jeweilig richtigen Datentyp bestückt wird.

MfG

TiltonJH

"In der Informatik geht es genauso wenig um Computer wie in der Astonomie um Teleskope."
Edsger W. Dijkstra

The Humble Programmer by Edsger W. Dijkstra